webpack书本总结,入门webpack必备
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack书本总结,入门webpack必备相关的知识,希望对你有一定的参考价值。
- 观看了《webpack实践,进阶调优》的书本,记下了一下认为重要的笔记,可能对你也有用
正文开始:
commonjs与esmodule区别:
- 1 commonjs模块依赖关系的建立发生在代码运行阶段; esmodule模块依赖关系的建立发生在代码编译阶段
- esmodule的优点
1 esmodule可以“死代码检测和排除”通过静态分析可以在打包时去掉这些未曾使用过的模块,以减小打包资源体积。
2 模块变量类型检查 ES6 Module的静态模块结构有助于确保模块之间传递的值或接口类型是正确的。
3 编译器优化 在CommonJS等动态模块系统中,无论采用哪种方式,本质上导入的都是一个对象,而ES6 Module支持直接导入变 量,减少了引用层级,程序效率更高。
-
2 值拷贝和动态映射
commonjs得到的是一个值的拷贝,可以看做 const a = require(xx) = const a = module.exports。 a直接指向了exports这个对象. 的地址。如果是原始对象,那么源文件的值修改了,引用的引文的值并不会改变 因为他们是值的拷贝,并不是映射。
而esmodule则是值的映射,并且映射是只读的。即使export const a = 1, import a from ‘x’,这里的a是对原🈶值的动态映射,也 就是当源文件a改变了,引入的这个a也会改变。但是不能在引入的文件
对映射值更改,可以理解成一个笼子加一张网,透过网可以看到里面的实时景像,但是不能通过网去更改里面的东西。
- 3 循环依赖
commonjs的循环依赖没有办法获取预期得到的结果。比如
// a.js
const b = require('./b')
console.log('b', b)
module.exports = a: 1
// b.js
const a = require('./a')
console.log('a', a)
module.exports = b: 2
// index.js
require('./a.')
正确的打印顺序应该是: ‘a’ : ; ‘b’: b:2
原因就是:看webpack的require实现原理
const cache =
function require(moduleId)
if(cache[moduleId])
// 已经加载过的直接到导出exports对象
return cache[moduleId].exports
var module = exports:
cache[moduleId] = module // 缓存
// ..执行该模块的代码给module赋值
// ...
return module.exports
commonjs使用了cache缓存,当加载过的时候会将模块缓存起来,第一次index.js就已经加载了a文件。a文件加载b文件,控制权到b文件,此时又去加载a文件,cache已经有a了,直接返回module.exports 而此时a文件还没执行完毕,exports对象指向的是,就导致这种结果了。
esmoudle循环加载呢?
打印结果是: ‘a’, undefined ; ‘b’: b:2
esmodule还是没能获取正确的值。原因也很简单,打印a的时候,此时a文件还没执行完毕,export default默认导出undefine,所以打印了undefined。
但是esmodule的特点是动态映射,也就是说,只要我们在打印a的时候,使用setTimeout延迟,此时b文件执行完毕,导出b:2,然后a文件再导出a:1,最后再打印a的时候,因为动态映射的关系就可以正确打印出来了。
区别三点:1 静态动态分析 2 值拷贝和值映射 3 循环引用的区别。
webpack 概念理解
-
entry: 入口
-
chunk: 每个entry入口文件,以及他所存在依赖关系的模块,都会在打包的时候封装成一个chunk代码块。
在Webpack中可以理解成被抽象和包装过后的一些模块。它就像一个装着很多文件的文件袋,里面的文件就是各个模块,Webpack在外面加了一层包裹,从而形成了chunk。多个入口有多个chunk生成。(特殊情况一个入口也可以有多个chunk)
-
bundle: chunk代码块打包后的文件称为bundle
资源输入和输出
-
entry
可以是 string | array | object | function
- 数组的作用是将多个资源预先合并,如
module.exports = entry: ['babel-polyfill', 'c', './src/index.js'] , ; 实际上是: module.exports = entry: './src/index.js', ; // index.js import 'c' import 'babel-polyfill';
会在最后一个index.js作为入口,然后前面的相当于在index.js中引入。
-
对象,可以定义多入口,属性名就是chunk名。如
module.exports = entry: index: ['babel-polyfill', './src/index.js'], lib: './src/lib.js', , ;
-
函数可以动态配置一些东西,也可以返回一个promise进行异步操作。
module.exports = entry: () => new Promise((resolve) => // 模拟异步操作 setTimeout(() => resolve('./src/index.js'); , 1000); ), ;
-
应用:单页面应用和多页面应用
1 单页面:只需要一个入口文件,公共模块提取vendor
module.exports = context: path.join(__dirname, './src'), entry: app: './src/app.js', vendor: ['react', 'react-dom', 'react-router'], , ;
将一些第三方模块一起打包,vendor并没有设置入口文件,可以设置CommonsChunkPlugin(4之后废弃),改用了optimization.splitChunks了。后续再做笔记。
2 多页面
module.exports = entry: pageA: './src/pageA.js', pageB: './src/pageB.js', pageC: './src/pageC.js', vendor: ['react', 'react-dom'] , , ;
也是需要配置optimizaiotn.splitChunks。
当第三方依赖较多时,我们可以用提取vendor的方法将这些模块打包到一个单独的 bundle中,以更有效地利用客户端缓存,加快页面渲染速度。
-
output出口
1 filename: 多个assets需要指定不同文件名,[hash] [chunkhash] [id] [name]
output: filename: '[name].js', ,
这些变量用来: 1对chunk进行区分 2 控制客户端缓存,chunkhash/hash当chunk内容改变的时候改变,文件名跟着改变,用户下一次请求资源文件的时候,文件名改变导致只能重新下载新的版本,而不用本地缓存。
一般用[name]@[chunkhash:8].js来进行命名,用于生产环境,开发环境则不需要。
2: publicPath
如果说path是用来指定资源的输出位置,那么publicPath就是用来指定资源的请求位置。
一般用于三种情况
1 html相关,可以指定publcikPath为html的相对路径,在请求的时候会以当前页面的index.html所在路径+相对路径,构成url,如 值为 '', '.xx'或者以'.'开头的 html地址:http://xx.com/app/index.html js文件名: a.js 当publciPath: '', 实例的路径就是http:xx.com/app/a.js publicPath: './js', 世纪就是http:xx.com/app/js/a.js
2 host相关,当值为'/'开头的,表示此时的publicPath以当前的host name为基础路径 如 publciPath: '/', 实际:http:xx.com/a.js publicPath: '/js/', 实际:http:xx.com/js/a.js
3 cdn 绝对路径 publicPath以协议头或相对协议的形式 publicPath: 'http://xx.cdn.com/', 实际就是http://.cdn.com/a.js
预处理器(loader)
loader赋予了webpack可处理不同资源类型的能力。
- loader的本质:函数,webpack4之前,输入和输出都必须为字符串,4之后,loader也支持了抽象语法书AST的传递,从而减少重复的代码解析
output = loader(input)
// input可能是字符串,也可能是上一个loader的结果,包括转化后的字符串,sourcemap, AST对象。
// output也可能是转化后的字符串,sourcemap,ast对象。
module.exports = function loader (content, map, meta)
var callback = this.async();
var result = handler(content, map, meta);
callback(
null, // error
result.content, // 转换后的内容
result.map, // 转换后的 source-map
result.meta, // 转换后的 AST
);
;
callback表示继续往下一个loader走,并传入对应参数。
- loader options: loader作为预处理器通常会给开发者提供一些配置项,在引入loader的时候可以通过 options将它们传入
rules: [
test: /\\.css$/,
use: [
'style-loader',
loader: 'css-loader',
options:
// css-loader 配置项
,
],
,
],
-
更多配置:
1 include & exclude 两者都存在,exclude权限更高
2 resource与issuer,被加载模块和加载模块,exclude和include,test本质上属于对被加载者的配置。
rules: [ test: /\\.css$/, use: ['style-loader', 'css-loader'], exclude: /node_modules/, issuer: test: /\\.js$/, include: /src/pages/, , ],
对加载者的限制,只有src下的pages下的js目录加载了css文件,规则才会生效,才会使用css-loader处理。
3 enforce:指定loader的种类,只接收pre post。
loader按照执行顺序分为: pre, inline(官方不推荐), normal, post四种类型。正常定义的loader都是noraml类型,可以通过enforce执行pre和post.
如对eslint-loader,就可以设置为p re,这样就可以在所有loader之前先进行代码检查。也可以不适用enforce而是自己保证loader的顺序是正确的即可。
enforce可以强制指定loader的作用顺序,可读性更强。
-
Babel-loader
babel-loader用来处理ES6+并将其编译为ES5,
npm install babel-loader @babel/core @babel/preset-env
babel-loader:它是使Babel与Webpack协同工作的模块 babel工作的环境。
@babel/core:它是Babel编译器的核心模块, 用来转换代码。
@babel/preset-env:它是Babel官方推荐的预置器,可根据用户设置的目标环境自动 添加所需的插件和补丁来编译ES6+代码,作用就是较core怎么转化。
rules: [ test: /\\.js$/, exclude: /node_modules/, use: loader: 'babel-loader', options: cacheDirectory: true, presets: [[ 'env', modules: false, ]], , , ],
cacheDirectory配置项启用缓存机制,可以接受一个路径,为true的时候,缓存目录为node_modules/.cache/babel-loader
preset-env会默认将esModule打包成commons,modules为false表示不转化,用于后期tree-shaking。
babel-loader支持从.babelrc文件读取Babel配置,因此可以将presets和plugins从 Webpack配置文件中提取出来,也能达到相同的效果。
-
ts-loader 他的配置项不在ts-loader中,而必须放入工程目录下的tsconfig.json中。
-
Html-load 将htmlt转为字符串,然后通过document.wirte写入到文档中。
-
file-loader默认以output的publicPath为路径,现在使用webpack5的asset了。
-
自定义loader loader的实现就是一个函数,
// loader.js module.exports = function(content) if(this.cacheable) this.cacheable() var useStrictPrefix = '\\'use strict\\';\\n\\n'; return useStrictPrefix + content; rules: [ test: /\\.js$/, use: loader: 'force-strict-loader', options: sourceMap: true, , , ],
这样就完成了一个lo ader,他的作用是在打包出来的文件前面加上严格模式。webpack可以使用htis.cacheable控制缓存,当文件和其他依赖没有变化的时候,不应该重复进行转换工作。
loader本质上是一个函数。第一个loader的输入是源文件,之后所有loader的输入是上 一个loader的输出,最后一个loader则直接输出给Webpack。
样式
-
PostCSS
使用postcss-loader可以轻松地将PostCSS与Webpack连接起来。使用npm进行安装。
module: rules: [ test: /\\.css/, use: [ 'style-loader', 'css-loader', 'postcss-loader', ] , ], ,
postcss要求有一个单独的配置文件,借助postcss-preset-env预设,自动添加前缀。配合stylelint检查样式
const postcssPresetEnv = require("postcss-preset-env"); const stylelint = require('stylelint'); module.exports = plugins: [ postcssPresetEnv( browsers: "last 5 version", ), stylelint( config: rules: 'declaration-no-important': true, , , ) ], ;
-
Css-module ,
module: rules: [ test: /\\.css/, use: [ "style-loader", loader: "css-loader", options: modules: true, localIdentName: "[name]__[local]__[hash:base64:5]", , , ], , ];
通过css-module开启modules开关,就可以启动模块化样式,localIdentName决定了转化后的类名,如
// style.css .titke color: red;
转化后的类型就是style_title_xxxx [name]是chunk名字, [local]是原本选择器表示符号,[hash]是hash值
以上是关于webpack书本总结,入门webpack必备的主要内容,如果未能解决你的问题,请参考以下文章