Webpack的进阶概念-CSS分割、Shimming与其他

1. CSS的分割

之前的文章中讨论主要围绕JS的代码分割和应用,这里我们来看一下针对CSS的分割

1.1 MinicssExtractPlugin介绍和使用

这个插件帮助我们将CSS压缩分割到多个文件中

这里我们先尝试新写一个style.css文件:

body{
    background:green;
}

然后打包,我们发现打包文件夹并没有该CSS文件,但是页面却渲染了对应的style,这是因为打包时将CSS打包到了JS文件内部,若我们希望将其打包成独立CSS文件,就需要借助这个插件

接着我们借助该插件进行打包,参考官方文档,这里我们去线上webpack配置文件里面使用,先引入,然后使用:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename:  '[name].css',
      chunkFilename: '[id].css'
    }),
  ],
  module: {
    rules: [
      {
          test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // 公共路径
              // 默认情况下,使用的是webpackOptions.output中publicPath
              publicPath: '../',
              //开发环境配置热更新
              hmr: process.env.NODE_ENV === 'development',
            },
          },
          'css-loader',
          'postcss-loader'
      }
    ]
  }
}

高级配置如下(既可以在开发中使用 HMR,也可以在生成版本的文件中提取样式):

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: devMode ? '[name].js' : '[name].[hash].css',
      chunkFilename: devMode ? '[id].css' : '[name].[hash].css',
    });
  ],
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: process.env.NODE_ENV === ‘development’
            }
          },
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      }
    ]
  }
}

打包之后若还是没有出现单独的css文件,这里有可能是因为tree-shaking的缘故:

你配置了optimaization和sideeffect,进行了tree-shaking,所以把css文件干掉了,因为里面的css属性没用到,这里我们可以对tree-shaking进行修改让其忽略css文件,取package.json进行修改:

"sideEffects":[
    "*.css"
]

1.2 filename和chunkFilename

上述filename和ChunkFilename配置项有什么区别呢?

这里我们举个例子,在有的框架中webpack配置文件你会看到如下的output字段:

output:{
    filename:'[name].js',
    chunkFilename:'[name].chunk.js'
}

这样打包之后我们会发现文件夹下除了main.js外还有一个vendors~lodash.chunk.js,为啥index.js打包之后遵循filename配置项,而lodash的chunk则遵循另外一个配置项呢?

这是因为main.js里面引入了lodash,lodash并不是入口js文件,而是异步加载的文件。

其实你也可以这样理解,打包的index.html只引入了main.js文件,这个就是入口,而lodash则是main.js里面间接引用的

上述css的两个filename也是同理~

1.3 生产环境优化压缩(production) - 合并压缩

这里可以借助optimize-css-assets-webpack-plugin这样的插件来优化压缩结果。

设置optimization.minimizer会覆盖 webpack 提供的默认值,因此确保要指定JS minimalizer:

//webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
  optimization: {
    minimizer: [new UglifyJsPlugin({
      include: /\/src/,
      exclude: /\/excludes/,
      cache: true,
      sourceMap: true,
      parallel: true,//多进程并行运行
      comments: false, //禁止构建注释
    }), new OptimizeCSSAssetsPlugin({})],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

另外这里可以通过splitChunks插件的cacheGroups概念来将所有 CSS 样式提取到单个文件中:

//webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

2. Shimming

打包过程中,我们往往需要处理一些打包过程中遇到的兼容性问题,例如之前polyfill解决低版本浏览器兼容性问题。其实这就是一个webpack的shimming-垫片。

当然这种兼容性不仅仅只是上述低版本兼容性。

2.1 ProvidePlugin插件

这里我们先看一个例子,我们新增一个库文件:

export function ui(){
    ${'body'}.css('background','red')
}

然后引入:

import $ from 'jquery';
import {ui} from './jquery.ui'

ui()

这里我们想一下他能不能执行呢?肯定是不行的,会报错jquery.ui.js文件里面找不到$对象,这是因为webpack的模块化打包策略导致A文件是不可能用B文件的导入变量的,为了避免耦合,出现相互影响!

除非我们在jquery.ui中也import $才行

但是这里我们可以借助这个插件实现Shimming行为来解决。

打开webpack的配置文件:

new webpack.ProvidePlugin({
    $:'jquery'
})

如果发现 符 号 , 那 么 会 自 动 引 入 j q u e r y 这 个 模 块 , 并 叫 做 符号,那么会自动引入jquery这个模块,并叫做 jquery,例如之前的jquery.js就会帮你自动import jquery

重新打包就会发现没有报错了。
这里就可以把这个插件理解成垫片。

例如我们希望直接用_join形式来调用_.join方法,我们就可以这样写:

new webpack.ProvidePlugin({
    $:'jquery',
    _join:['lodash','join']
    
})

2.2 imports-loader

这里再来看一个loader来实现垫片行为

我们在index.js使用的this指向谁?其实是这个模块自身,有时候我希望this能够指向window,当然这里是不等于window全局变量的,能不能修改呢?

这里可以借助import-loader来解决:(记住首先需要安装)

rules:[{
    test:/\.js$/,
    use:[{
        loader:'babel-loader'
    },{
        loader:'imports-loader?this=>window'
    }]
}]

上述就是标准写法,然后交给babel-loader进行编译就可以了。

然后就可以看到this指向变了,就是window对象了~

上述行为也叫做shimming行为,垫片是一种行为,上述都是这种概念

3. content-hash

这个webpack相关概念主要用于解决浏览器缓存导致客户端展示旧页面的问题。

有时候我们修改了index.js,重新打包上传,刷新浏览器看到的还是老的页面,这是因为文件名字没有变,所以会去拿缓存,所以为了解决浏览器缓存问题,引入了content-hash概念

开发环境下其实无所谓,但是线上环境的打包配置文件就需要修改了:

//webpack.prod.js
output:{
    filename:'[name].[contenthash].js',
    chunkFilename:'[name].[contenthash].chunk.js'
}

其实就是根据content生成的一个hash值,只要content不变,hash就不变,名称就不变。

这样用户访问新的页面就会加载新的main.js文件,然后vendors.js这些库的chunk会使用缓存,通过这种形式用户就只会重新加载会更新的JS代码,没变的就用缓存~

4. 环境变量

这里直接通过一个webpack设置开发和线上配置文件的实例来说明环境变量的应用

我们修改一下原先的webpack.dev.js和webpack.prod.js文件,不再引入common内容,也不进行merge

接着打开common.js配置文件,引入merge和devConfig与prodConfig:

const devConfig = require('./webpack.dev.js')
const prodConfig = require('./webpack.prod.js')

不直接导出common的config对象,改为一个变量:

const commonConfig = {
    ....(原先common配置对象)
}

最后导出一个根据env变量来进行一个mergeConfig操作:

module.exports = (env) =>{
    if(env && env.production){
        return merge(commonConfig,prodConfig)
    }else {
        merge(commonConfig,devConfig)
    }
}

其实上述操作的目的就是把原先的环境判断从原先的通过不同JS文件进行改为通过一个env变量进行

接着我们需要传递一个env这个环境变量,去package.json文件,这里在scripts里面新增环境变量,同时把目标配置文件都改成webpack.common.js:

"scripts":{
    "dev-build":"webpack --config ./build/webpack.common.js",
    "dev":"webpack-dev-server --config ...",
    "build":"webpack --env.production --config ..."
}

通过添加–env.production来添加这个属性,默认为true。而不填的默认不存在。

你也可以指定具体的值:

--env.production = abc

if(env && env.produciton === 'abc')

猜你喜欢

转载自blog.csdn.net/sdsh1880gm/article/details/108941622
今日推荐