关于webpack

137 阅读3分钟

webpack

常用loader

  • css-loader 处理css结尾的文件

  • style-loader 将样式内嵌在htmlheader内

  • file-loader 以外链方式加载样式

  • sass-loader

  • less-loader

  • postcss-loader

注:loader处理从右到左

常用plugin

  • html-webpack-plugin 自动生成html文件
  • clean-webpack-plugin 自动清除目录内的文件

开发

  • source-map devtool: 'inline-source-map'

  • 开发工具 webpack watch mode

    "scripts":{
        "watch": "webpack --config webpack.config.js --watch"
    }
    

    webpack-dev-server(使用最多)

    // webpack.config.js
    module.exports = {
        ...
        devServer:{
            contentBase: path.resolve(__dirname,'dist')
        }
    }
    // package.json
    {
        "scripts":{
            "dev": "webpack-dev-server --open"
        }
    }
    

    webpack-dev-middleware

    webpack-dev-middleware是一个容器,它可以把webpack处理后的文件发送到一个服务器,webpack-dev-server在内部使用了它,也可以单独拿出来
    

热模块替换(HMR)

它允许在运行时更新各种模块,而无需进行完全刷新

  • 启用

    devServer:{
    	hot: true
    }
    

tree-shaking

移除上下文中的未引用代码,通过package.json的“sideEffects”属性作为标记,像compiler提供提示,表明项目中的哪些文件是pure(纯es2015模块),由此可以安全地删除文件中使用的部分

// package.json
{
	"sideEffects": false
}
// 任何导入的文件都会受到treeShaking影响,这意味着如果在项目中使用类似css-loader并导入css文件,则需要将其添加到sideEffect列表中,以避免在生产模式中无意中将它删除
{
	"sideEffects":[
		"./src/project/project.js""*.js"
	]
}

压缩输出

使用webpack编译标记,来启用uglifyjs压缩插件

module.exports = {
	mode: 'production'
}

生产环境构建

// webpack.common.js
{
    entry:{},
	plugins:[
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin()
    ],
    output:{
        filename:'[name].bundle.js',
        path:path.resolve(__dirname,'dist')
    }
}
// webpack.dev.js
{
    devTool: 'inline-source-map'
    //避免在生产中使用* `inline-***` *和* `eval-***`*,因为它们可以增加 bundle 大小,并降低整体性能。*
    devServer:{
        contentBase: path.resolve(__dirname,'dist'),
        hot: true
    }   
}
// webpack.prod.js
const merge = require('webpack-merge')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const common = require('./webpack.common.js')
module.exports = merge(common,{
    plugins:[
        new UglifyJSPlugin()
    ]
})
// 指定环境
const webpack = require('webpack');
module.exports = {
    plugins:[
        new webpack.DefinePlugin({
            'process.env.NODE_ENV':JSON.stringify('production')
        })
    ]
}
// split css

代码分离

CommonsChunkPlugin

CommonsChunkPlugin已经从webpack v4 legato中移除,如何处理chunk,请使用SplitChunksPlugin

动态导入

function getComponent(){
    return import(/* webpackChunkName: 'lodash' */ 'lodash').then(_ =>{
        const el = document.createElement('div')
        el.innerHTML = _.join(['hello'])
        return el
    }).catch(err => 'an error')
}

vue 懒加载

  • lazy load in components
Vue.Component('AsynCmp',() => import("./AsyncCmp"))
new Vue({
    components:{
		AsyncCmp: () => import('./asyncCmp')
    }
})
  • lazy load in VueRouter
const login = () => import("./login")
new VueRouter({
	routes:[{
		path: '/login',
		component: login
	}]
})
  • lazy load in Vuex module
const store = new Vuex.Store()

import('./store/login').then(loginModule => {
	store.registerModule('login', loginModule)
})

缓存

  • contenthash

    资源内容发生变化时,contenthash也会发生变化

  • 第三方库提取到单独vendor

    可使用 optimization.runtimeChunk 选项将 runtime 代码拆分为一个单独的 chunk。将其设置为 single 来为所有 chunk 创建一个 runtime bundle

module.exports = {
	optimization:{
		runtimeChunk: 'single',
		splitChunks:{
			cacheGroups:{
				vendor:{
					test: /[\\/]node_modules[\\/]/,
					name: 'vendors',
					chunks: 'all'
				}
			}
		}
	}
}
  • 模块标识符

    添加print.js打包后,文件的 hash 都变化了。这是因为每个 module.id 会默认地基于解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变。因此,简要概括:

    • main bundle 会随着自身的新增内容的修改,而发生变化。
    • vendor bundle 会随着自身的 module.id 的变化,而发生变化。
    • manifest bundle 会因为现在包含一个新模块的引用,而发生变化。
vendor的hash变化是要修复的
const webpack = require('webpack')
module.exports = {
	plugins:[
		new webpack.HashModuleIdsPlugin()
	]
}

创建library

// 初始化
npm init -y 
npm i -S webpack lodash
// src/index.js
import _ from 'lodash'
import numRef from './ref.json'
export  function numToWord(num){....}
export function wordToNum(word){...}
// library调用规范 es2015
import * as webpackNumbers from 'webpack-numbers'
webpack.wordToNum('two')
// 外部化lodash
module.exports = {
	entry:'',
	output:{},
	externals:{
		lodash:{
			commonjs: 'lodash',
			root: '_'
		}
	}
}

shim预置全局变量

​ 使用ProvidePlugin后,能够在webpack编译的每个模块中,通过访问一个变量来获取一个package,webpack看到模块中用到这个变量,它将在最终bundle中引入给定package

// src/index.js
function component(){
	const element = document.createElement('div')
	element.innerHTML = _.join(['a','b'])
	return element
}
document.body.appendChild(component())
// webpack.config.js
const webpack = require('webpack')
module.exports = {
	plugins:[
        new webpack.ProvidePlguin({
            _: 'lodash'
        })
        //或者使用数组形式
        new webpack.providePlugin({
        	join:['lodash','join']
    		// 只要调用join方法就会使用lodash内的方法,这样就能很好的与tree shaking配合,将lodash libary中的其余没有用到的导出去除
        })
    ]
}

TypeScript

  • 基础配置
npm install -D typescript ts-loader

​ 项目中添加tsconfig.json

环境变量

// webpack.config.js
module.exports = env => {
    console.log(env.NODE_ENV)
}

构建性能

对最少数量的必要模块使用 loader。不应该如下:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  }
};

而是使用 include 字段仅将 loader 应用在实际需要将其转换的模块所处路径:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        loader: 'babel-loader'
      }
    ]
  }
};