最近在学习webpack, 但感觉刚开始看完一遍过段时间又全忘了,所以打算将一些学习的总结与思考记录到博客中,希望对自己的提升有所帮助。
1.什么是模块化?
模块化开发一直是前端工程中最常见的名词了,虽然可能都大致明白是什么意思,但用语言如何表述呢?
假设一台手机或者家电坏了拿到店里找老师傅维修,老师傅可以直接检测一下电视内部的哪个模块坏了直接换一块新的就可以了。而所谓的模块化开发就是造这样的一台电视,当我们的代码需要更新或者维护时,可以很快的找到对应的模块进行修改或者替换。
同时模块化不仅仅使前端工程更加清晰,同时还具有可复用的功能,前端模块化通常指的是一个代码块的最小甚至是最有组合,常见的例如npm包。如果没有模块,我们可能会编写代码更多的就是copy,copy多了,代码的可维护性就会下降。
常见的模块化规范有commonJS(require, export, module)、AMD(不同熟)、es6 Module(import, export)。
2.什么是工程化
当web应用越来越复杂时,我们会发现更多问题:
1.模块间的依赖如何管理
2.多页面、多系统、多状态怎么办
3.如何进行团队合作
4.怎么解决多人研发的性能问题,代码风格问题?
这些就是需要使用webpack的原因。现在的webpack更像是从一套解决Javascript模块化依赖打包开始,利用强大的插件机制,逐渐解决前端资源依赖管理问题。
3webpack解决的问题是什么
1.按需加载(后面详述)
2.模块化打包,一切皆模块
3.语法转化,使用babel, es6转es5
4.css、图片资源等处理
5.压缩优化, CDN等
6.eslint统一团队代码风格
…
以下先谈谈webpack的一些基本概念,这部分详细见官网。
4.webpack的配置
webpack的配置一般统一放在webpack.config.js文件内,该配置文件是遵循Node.js的CommonJS模块规范的,通过require()语法导入其它文件。示例如下:
const path = require('path');
module.exports = {
mode: 'development',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js'
}
};
默认情况下,webpack会查找执行目录下的webpack.config.js作为配置,如果需要重指定配置文件也可使用如下命令:
webpack --config webpack.config.js
或者使用npx
npx webpack --config webpack.config.js
5.mode
webpack的模式分为production和development两类环境,通过mode指定是开发环境还是生产。
6.context
context表示项目打包路径的上下文,如果指定context的路径,则配置中的路径都是相对context指定的路径。context值必须是一个绝对路径。实际开发中大多情况不需要配置。
7.entry和output
entry支持多种类型:字符串、对象、数组。
作用上有单文件入口和多文件入口。单文件即entry配置项的为字符串或数组时。配置项为对象时为多入口文件。多入口文件的配置常用于多页应用。
module.exports = {
entry: {
home: 'path/to/my/entry/home.js',
search: 'path/to/my/entry/search.js',
list: 'path/to/my/entry/list.js'
}
};
output指定entry对应文件编译打包后的输出,一个项目可以有多个entry,但只能有一个output,不同的entry可以通过output.filename占位符来区分:
module.exports = {
entry: {
home: 'path/to/my/entry/home.js',
search: 'path/to/my/entry/search.js',
list: 'path/to/my/entry/list.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
8.output.publicPath
output.publicPath在访问外部静态资源时是一个很重要的选项,它能决定html-webpack-plugin打包生成的HTML文件内的src或者link的访问路径.
module.exports = {
output: {
path: '/home/git/public/assets',
publicPath: '/assets/'
}
};
输出结果:
<head>
<link href="/assets/logo.png" />
</head>
output.path指定输出到本地磁盘的路径,output.publicPath则是实际上线到服务器后的url地址,一般在静态资源需要上CDN的时候会进行配置。
9.externals
externals的作用是用于去除待打包文件在依赖的某些第三方库,从而减小打包体积。该功能通常会在开发自定义的js库的时候会用到,主要是为了去除js库中依赖的第三方库。这些依赖的库应该由使用者去提供,而不应该在开发的js库中去引入。
10.devtool
devtool是控制sourcemap的显示的,通过soucemap我们可以用于定位代码的错误位置。soucemap的生成会消耗打包的时间,打包速度和特点
一般生产环境使用source-map,开发环境使用cheap-module-eval-map.
11.resolve
resolve的作用主要帮助我们查找模块依赖模块。
(1)resolve.extensions(解析扩展名)
resolve.extensions是帮助解析扩展名,默认值为:[’.wasm’, ‘.mjs’, ‘.js’, ‘.json’],
module.exports = {
resolve: {
extensions: ['.js', '.json', '.css']
}
};
当我们引入js或者css文件时就可以不写上扩展名
(2)resolve.alias(快速查找依赖)
alias主要针对目录结构层次比较深时,可以通过alias来缩短import引入文件时的写法。
module.exports = {
resolve: {
alias: {
src: path.resolve(__dirname, 'src'),
'@lib': path.resolve(__dirname, 'src/lib')
}
}
};
12.module
webpack解析模块的同时,不同的模块需要使用不同的类型的模块处理器,这部分的设置就在module配置中。
(1)module.noParse
让webpack忽略对部分没采用模块化的文件递归解析和处理,这样做的好处是提高构建性能。接收类型为正则表达式。
module.exports = {
module: {
// 使用正则表达式
noParse: /jquery|lodash/
// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content) => {
// content 代表一个模块的文件路径
// 返回 true or false
return /jquery|lodash/.test(content);
}
}
}
(2)module.parser(控制模块化的语法)
parse可以控制项目中各模块的语法是否解析。
module: {
rules: [{
test: /\.js$/,
use: ['babel-loader'],
parser: {
amd: false, // 禁用 AMD
commonjs: false, // 禁用 CommonJS
system: false, // 禁用 SystemJS
harmony: false, // 禁用 ES6 import/export
requireInclude: false, // 禁用 require.include
requireEnsure: false, // 禁用 require.ensure
requireContext: false, // 禁用 require.context
browserify: false, // 禁用 browserify
requireJs: false, // 禁用 requirejs
}
}]
}
(3)module.rules
主要通过规则来匹配模块,提交给对应的处理器处理,通常使用在配置loader中。rule大致组成为:
a.条件匹配:通过test、include、exclude等配置命中应用规则的模块文件
b.应用规则:对匹配条件通过后的模块,使用use配置来应用loader
c.重置顺序:一组loader的顺序是从后往前执行,通过enforce可以让其中的一个loader的执行顺序放到最前(pre)或者最后(post)