前端杂谈
zichil / zichil.github.io Goto Github PK
View Code? Open in Web Editor NEW前端学习笔记
前端学习笔记
前端杂谈
升级 webpack4 已经有一段时间了,将升级过程中记录下来。
将 Webpack 升级到 4.x 之后,直接运行webpack -xx,会提示还需要安装 webpack-cli。
The CLI moved into a separate package: webpack-cli. Please install webpack-cli in addition to webpack itself to use the CLI.
npm i webpack@latest webapck-cli -D
接下来升级所有的依赖,查看所有可以更新的包,进行升级。
npm outdate
module.exports = {
+ mode: 'development'
- devtool: 'eval',
- plugins: [
- new webpack.NamedModulesPlugin(),
- new webpack.NamedChunksPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
production 模式下,由于提供了 splitChunks 和 minimize,所以基本零配置,代码就会自动分割、压缩、优化,同时 webpack 也会自动帮你 Scope hoisting 和 Tree-shaking。
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
DefinePlugin 允许创建一个在编译时可以配置的全局常量。 在此处设置可以全局访问的环境变量 process.env.NODE_ENV,以便做相应不同的操作 。
所谓的“零配置”就是通过设置mode,自动配置两个环境通常需要做的操作。
webpack4 更推荐使用 mini-css-extract-plugin 替代 extract-text-webpack-plugin。该插件将CSS提取到单独的文件中。它为每个包含CSS的JS文件分离出一个CSS文件,并且支持CSS和SourceMap的按需加载。
css 原本是内联在 js 文件中的,之所以要将 css 独立拆包,是为了让 css 和 js 的改动互不影响,比如改了css,只会让 css 的缓存失效,而不会让 js 的缓存失效。并且它还会根据 optimization.splitChunks 的配置来自动拆包,比如将 element-ui 单独拆分出来作为一个 bundle, 那么 element-ui 的 css 也被单独拆分出来,而不是像原来将所有 css 合并成一个文件,动辄几百 KB 的大小。
与 extract-text-webpack-plugin 相比:
// webpack.prod.conf.js
module.exports = merge(baseWebpackConfig, {
plugins: [
- new MiniCssExtractPlugin({
- filename: utils.assetsPath('css/[name].[contenthash].css')
- }),
]
}
// utils.js
// Extract CSS when that option is specified
// (which is the case during production build)
- if (options.extract) {
- return ExtractTextPlugin.extract({
- use: loaders,
- fallback: 'vue-style-loader',
- publicPath: '../../'
- })
- } else {
- return ['vue-style-loader'].concat(loaders)
- }
+ return [
+ options.extract ? {
+ loader: MiniCssExtractPlugin.loader,
+ options: {
+ publicPath: '../../'
+ }
+ } : 'vue-style-loader',
+ ].concat(loaders)
将 vue-loader 升级到15.x之后,需要增加如下代码:
const { VueLoaderPlugin } = require('vue-loader')
plugins: [
new VueLoaderPlugin(),
]
下面是 webpack4 默认的 optimization 配置。
+ mode: 'development'
- optimization: {
- namedModules: true,
- namedChunks: true,
- nodeEnv: 'development',
- flagIncludedChunks: false,
- occurrenceOrder: false,
- sideEffects: false,
- usedExports: false,
- concatenateModules: false,
- splitChunks: {
- hidePathInfo: false,
- minSize: 10000,
- maxAsyncRequests: Infinity,
- maxInitialRequests: Infinity,
- },
- noEmitOnErrors: false,
- checkWasmTypes: false,
- minimize: false,
- removeAvailableModules: false
- },
+ mode: 'production',
- optimization: {
- namedModules: false,
- namedChunks: false,
- nodeEnv: 'production',
- flagIncludedChunks: true,
- occurrenceOrder: true,
- sideEffects: true,
- usedExports: true,
- concatenateModules: true,
- splitChunks: {
- hidePathInfo: true,
- minSize: 30000, // 压缩前 chunk 的体积大于等于300000 byte
- maxAsyncRequests: 5, // 按需加载 bundle 的并发请求数小于等于5
- maxInitialRequests: 3, // 页面初始加载 bundle 的并发请求数小于等于3
- },
- noEmitOnErrors: true,
- checkWasmTypes: true,
- minimize: true,
- },
webpack4 其中一个重要改变就是 optimization.splitChunk 替代原先的 webpack.optimize.CommonsChunkPlugin。
只要设置 mode: production,webpack4 就会自动开启 splitChunk 进行 Code Splitting。默认的 splitChunk 的配置如上。
Code Splitting 的目的就是为了更好的利用浏览器缓存。虽然 webpack4 默认的拆包已经很不错,但是根据自己项目的情况还是可以进行优化。
如 vue,vuex,vue-router,axios 等第三方库,是项目构成的基础,每个页面都有用到,并且升级频率不高,除了组件库之外还有一些体积不大的第三方库,这些都放在这个包里。
项目中使用的组件库是 element-ui,由于几乎所有的组件都有用到,所以没有做按需引入。体积很大,没 Gzip 之前有658.86 KB,Gzip 之后也有160多 KB。比整个 vendor 的体积都大,并且更新频率也比 vendor 里的高。如果不将 element -ui 分割出来,一旦更新,那么整个 vendor + element-ui
体积大小的 bundle 要重新从服务器获取资源,不能利用缓存。因此将组件库从 vendor 中分割了出来。
我们平时写的业务代码。项目中使用了路由懒加载,通过 component: () => import(...) 引入路由组件,webpack 就会根据路由自动进行代码分割。
我们还需要将存有 chunk 之间映射关系的 runtime 代码提取出来,否则它将包含在 bundle 之中。manifest 描述了 webpack 应该加载哪些文件,提取出来可以更快的加载文件而不用等待 bundle 加载。并且,哈希值是根据 chunk 内容生成的,一旦哈希值改变,runtime 代码也会改变,那么runtime 所在的 bundle 也将缓存失效。
manifest 提出出来得到的文件通常很小,并且这个文件每次上线都会改变,需要为了几 KB 浪费一次请求数。因此更推荐将 manifest 内联到 html 文件中,毕竟 html 文件 是每次都会改变的。使用的插件是 inline-manifest-webpack-plugin。
根据上面所说的我们可以做出如下配置
runtimeChunk: {
name: 'manifest',
},
splitChunks: {
chunks: 'all',
cacheGroups: {
// 抽离第三方插件
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'initial',
name: 'vendor',
},
'vendor-element-ui': {
name: 'vendor-element-ui', // split elementUI into a single package
priority: 20, // the weight needs to be larger than vendor and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
enforce: true,
},
},
},
将 css 分离出来后,还需要对 css 做压缩与优化。这是为什么还需要optimize-css-assets-webpack-plugin。
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin()]
}
optimize-css-assets-webpack-plugin 默认使用 cssnano 来做 css 优化,压缩代码,删掉无用的注释,去掉冗余的代码,还会优化代码及书写顺序。因此大大减少了 css 文件的大小。
在 mode: production 模式下,会自动开启 minimize: true,自动压缩代码,内置的插件就是 terser-webpack-plugin,但是 terser-webpack-plugin 的默认配置并不能满足我们的项目。
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
drop_console: true,
safari10: true,
},
sourceMap: config.build.productionSourceMap,
parallel: true,
cache: true,
}),
]
}
版本 | 第一次 | 第二次 | 第三次 | 第四次 | 第五次 |
---|---|---|---|---|---|
webpack3 | 49324 | 52749 | 49431 | 51893 | 49725 |
webpack4 | 39034 | 33620 | 31968 | 30814 | 31874 |
可以看到打包速度有了质的飞跃,大幅降低上线所需要的时间。
从输入url开始的过程
页面加载的性能优化
虽然长连接可以使一个TCP连接请求多个资源,但是我们请求的资源往往是有关联的,比如要先请求完1.css 再请求1.js
浏览器一般会建立多个TCP连接,并行请求资源,从而加快整体的速度,但是并行请求的数量是有限制的
针对以上两点,我们还是需要去做一些优化。
减少HTTP请求的次数,所以就有了代码合并,雪碧图,将图片转为base64写入其他文件
使用HTTP 2,提供了多路复用的功能,所有资源都可以通过一个TCP连接不阻塞的并行传输,就不需要再做减少HTTP请求次数的优化了。
减少请求资源的大小:开启Gzip可以大幅减少资源体积,分割代码并启用路由(组件)懒加载,优化首屏加载的性能,使用合理的图片大小和格式比如说谷歌开发的图片格式WebP,或者直接用字体图标代替图片,字体图标不仅体积小,而且可以任意变化大小不会模糊,还可以任意变化颜色,使用非常灵活
缓存,不需要接受数据的传输,直接使用浏览器的本地缓存,分为两种:强缓存和协商缓存,一般是由上一次请求服务器返回的响应头控制。
强缓存:不会向服务器发送请求,Cache-control或者Expires判断,如果命中,就返回状态码200;
如果不命中则是判断是否协商缓存:向服务器发送验证请求,如果请求头有If-Modified-Since,服务器返回Last-Modified,如果两个相同则返回状态码304;如果发送的请求头有If-None-Match,服务器返回ETag,比较两者,如果相同则返回状态码304
需要注意的是强缓存则不会与服务器有任何交互,所以服务器更新了资源,浏览器也无法得知,如果每次请求都使用协商缓存,每次都要向服务器发送验证请求,又多做了无用功,所以我们应该尽可能的使用强缓存,但是在更新版本之后让缓存失效,所以在更新版本的时候直接修改资源文件名就可以实现了,然后webpack可以让我们在打包的时候自动给文件名加上哈希值。
页面渲染过程
页面渲染的性能优化
问题:
优化:
dom:减少dom元素的数量,降低DOM的深度,减少生成DOM树和渲染树的构建成本,尽量避免塞进去div元素只为了实现布局,可以用伪元素的就用伪元素
js:1. 将js放在body底部
2. 减少以及合并DOM操作,使用DocumentFragment进行DOM的离线更新
css:1. 放在head中,避免html渲染完成后,又重新结合CSSOM树,引起页面闪烁
2. 降低css的层级和选择器的复杂度
3. 使用更高性能的css样式,比如用flex布局代替float布局
4. 合理使用脱离文档流的样式,比如absolute,fiexd,减少引发回流
5. 优先使用transform,opacity等属性来实现动画,可以跳过布局和绘制的过程,计算样式之后直接进行渲染层合并
链表是数据元素的线性集合,但他不是像数组那样连续储存在内存中,每个链表元素都由他自己的值和指向下一个元素的引用组成。他可以插入或删除指定的元素,但由于他的访问只能是线性的,它要访问一个元素,就要从头开始遍历直到找到该元素,不能像数组那样快速的随机访问。
Add(value)
Pre: value is the value to add to the list
Post: value has been placed at the tail of the list
n ← node(value)
if head = ø
head ← n
trail ← n
else
trail.next = n
trail ← n
end if
end Add
Prepend(value)
Pre: value is the value to add to the list
Post: value has been placed at the head of the list
n ← node(value)
n.next ← head
n ← head
if trail = ø
trail ← n
end
end Perepend
Contains(head, value)
Pre: head is the head node in the list
value is the value to search for
Post: the item is either in the linked list, true; otherwise false
n ← head
if n = ø
return false
while (n != ø and n.value != value)
n ← n.next
end while
if (n = ø)
return false
end if
return true
Remove(head, value)
Pre: head is the head node in the list
value is the value to remove from the list
Post: value is removed from the list, true, otherwise false
n ← head
if n = ø
return false
end if
if n.value = value
if head = trail
head ← ø
trail ← ø
else
head ← head.next
return true
end if
while n.next != ø and n.next.value != value
n ← n.next
end while
if n.next != ø
if trail = n.next
trail ← n
end if
n.next ← n.next.next
return true
end if
else
return false
end Remove
Traverse(head)
Pre: head is the head node in the list
Post: the items in the list have been traversed
n ← head
while n != ø
yield n.value
n ← n.next
end while
end Traverse
ReverseTraversal(head, trail)
Pre: head and tail belong to the same list
Post: the items in the list have been traversed in reverse order
if trail != ø
curr = trail
while head != curr
prev ← head
while prev.next != curr
prev ← prev.next
end while
yield curr.value
curr ← prev
end while
yield curr.value
end if
end ReverseTraversal
访问 | 搜索 | 插入 | 删除 |
---|---|---|---|
O(n) | O(n) | O(1) | O(n) |
O(n)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.