文末
从转行到现在,差不多两年的时间,虽不能和大佬相比,但也是学了很多东西。我个人在学习的过程中,习惯简单做做笔记,方便自己复习的时候能够快速理解,现在将自己的笔记分享出来,和大家共同学习。
个人将这段时间所学的知识,分为三个阶段:
第一阶段:HTML&CSS&JavaScript基础
第二阶段:移动端开发技术
第三阶段:前端常用框架
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
-
推荐学习方式:针对某个知识点,可以先简单过一下我的笔记,如果理解,那是最好,可以帮助快速解决问题;
-
大厂的面试难在,针对一个基础知识点,比如JS的事件循环机制,不会上来就问概念,而是换个角度,从题目入手,看你是否真正掌握。所以对于概念的理解真的很重要。
同时url-loader会根据默认的(或者配置的)limit参数,将满足条件的图片转换成base64格式,并打包到js里,从而减小http请求,提高页面加载速度。但是这种操作只针对小体积的图片,不适用大图片。
module: {
rules: [
{
test: /.(png|jpe?g|gif)$/,
use: {
loader: ‘url-loader’,
options: {
name: ‘[name]_[hash].[ext]’,
outputPath: ‘images/’,
// 小于2048Byte(2KB),才转换成base64
limit: 2048
}
}
}
]
}
- css-loader
分析css模块之间的关系,并合成⼀个css
- style-loader
style-loader可以会把css-loader生成的内容,以style挂载到页面的head部分
- less-loader
less-loader会把less语法转成css
在多loader转换时,执行顺序为从右到左,从下到上
module: {
rules: [
{
test: /.css$/,
use: [‘style-loader’, ‘css-loader’]
},
{
test: /.scss$/,
use: [‘style-loader’, ‘css-loader’, ‘less-loader’]
}
]
}
- postcss-loader
postcss-loader也比较常见,通过用来批量转化css,比如:自动添加前缀以适配不同版本的浏览器,具体的浏览器适配规则可以参见https://caniuse.com/
// webpack.config.js中的配置
module: {
rules: [
{
test: /.css$/,
use: [‘style-loader’, ‘css-loader’, ‘postcss-loader’]
}
]
}
// 此外,需要新建postcss.config.js
module.exports = {
plugins: [
require(‘autoprefixer’)({
// last 2 versions: 最近的两个大版本
// >1%: 全球市场份额大于1%的浏览器
overrideBrowerslist: [‘last 2 versions’, ‘>1%’]
})
]
}
babel-loader
在使用babel-loader,我们需要了解几个概念
-
Babel是JavaScript编译器器,能将ES6代码转换成ES5代码,让我们开发过程中放⼼使用JS新特性⽽不用担⼼心兼容性问题。并且还可以通过插件机制根据需求灵活的扩展。
-
babel-loader是webpack与babel的通信桥梁,不做把es6转成es5的⼯作,这部分⼯作需要用到
@babel/preset-env
来做,@babel/preset-env里包含了es6,7,8转es5的转换规则
-
在使用preset-env时,需要注意的就是按需引入,由配置参数
useBuiltIns
决定 -
useBuiltIns选项是babel7的新功能,这个选项告诉babel如何配置@babel/polyfill。 它有三个参数可以使⽤:
-
entry:需要在webpack的⼊口文件里
import "@babel/polyfill"
⼀次。 babel会根据你的使⽤情况导⼊垫片,没有使⽤的功能不会被导入相应的垫⽚。 -
usage:不需要import,全⾃动检测,但是要安装@babel/polyfill。(试验阶段)
-
false:如果你
import "@babel/polyfill"
,它不会排除掉没有使用的垫⽚,程序体积会庞⼤。(不推荐) -
请注意: usage的⾏为类似 babel-transform-runtime,不会造成全局污染,因此也不会对类似 Array.prototype.includes() 进行 polyfill。
[
“@babel/preset-env”,
{
targets: { // 需要指定代码运行的浏览器环境
edge: “17”,
firefox: “60”,
chrome: “67”,
safari: “11.1”
},
corejs: 2,// 新版本需要指定核心库版本
useBuiltIns: “usage”// 按需注入
}
]
-
默认的Babel只⽀持let等⼀些基础的特性转换,Promise等⼀些还有转换过来,这时候需要借助垫片
@babel/polyfill
,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性,其原理是语法转换,也就是说在转换后的文件里定义一个promise,并挂载到window对象上。 -
Babel在执⾏编译的过程中,会从项⽬根⽬录下的
.babelrc
JSON 文件中读取配置。没有该⽂件会从loader的options地⽅读取配置
//.babelrc
{
presets: [
[
“@babel/preset-env”,
{
targets: { // 需要指定代码运行的浏览器环境
edge: “17”,
firefox: “60”,
chrome: “67”,
safari: “11.1”
},
corejs: 2,// 新版本需要指定核心库版本
useBuiltIns: “usage”// 按需注入
}
]
]
}
//webpack.config.js
{
test: /.js$/,
exclude: /node_modules/,
loader: “babel-loader”
}
这是最后的全部配置
module: {
rules: [
test: /.js$/,
exclude: /node_modules/,
use: {
loader: “babel-loader”,
options: {
presets: [
[
“@babel/preset-env”,
{
targets: { // 需要指定代码运行的浏览器环境
edge: “17”,
firefox: “60”,
chrome: “67”,
safari: “11.1”
},
corejs: 2,// 新版本需要指定核心库版本
useBuiltIns: “usage”// 按需注入
}
]
]
}
}
]
}
======================================================================
HtmlWebpackPlugin
简化了HTML文件的创建,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。 你可以让插件为你生成一个HTML文件,使用lodash模板提供你自己的模板,或使用你自己的loader。
// 配置参数
title: ⽤来⽣成⻚面的 title 元素
filename: 输出的 HTML ⽂件名,默认是 index.html, 也可以直接配置带有子目录。
template: 模板⽂件路路径,⽀持加载器,⽐如 html!./index.html
inject: true | ‘head’ | ‘body’ | false ,注⼊所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元 素的底部,‘head’ 将放置到 head 元素中。
favicon: 添加特定的 favicon 路径到输出的 HTML ⽂文件中。 minify: {} | false , 传递 html-minifier 选项给 minify 输出
hash: true | false, 如果为 true, 将添加⼀个唯一的 webpack 编译 hash 到所有包含的脚本和 CSS 文件,对于解除 cache 很有用。
cache: true | false,如果为 true, 这是默认值,仅仅在⽂件修改之后才会发布⽂件。
showErrors: true | false, 如果为 true, 这是默认值,错误信息会写入到 HTML ⻚面中。
chunks: 允许只添加某些块 (⽐如,仅 unit test 块) chunksSortMode: 允许控制块在添加到⻚面之前的排序方式,支持的值:‘none’ | ‘default’ | {function}-default:‘auto’ excludeChunks: 允许跳过某些块,(比如,跳过单元测试的块)
// 使用案例
const path = require(‘path’)
const htmlWebpackPlugin = require(‘html-webpack-plugin’)
module.exports = {
…
plugins: [
new htmlWebpackPlugin(
// 插件参数传递,使用对象
{
title: ‘my App’,
filename: ‘index.html’,
template: ‘./src/index.html’
}
)
]
}
// index.html 中获取插件参数
其实 clean-webpack-plugin 很容易知道它的作用,就是来清除文件的。
一般这个插件是配合 webpack -p
这条命令来使用,就是说在为生产环境编译文件的时候,先把 build或dist
(就是放生产环境用的文件) 目录里的文件先清除干净,再生成新的。
// 使用案例
const cleanWebpackPlugin = require(‘clean-webpack-plugin’)
module.exports = {
…
plugins: [
new cleanWebpackPlugin()
]
}
一般我们的 css 是直接打包进 js⾥面的,我们希望能单独⽣成 css文件。 因为单独⽣成css,css可以和js并行下载,提高⻚面加载效率
借助MiniCssExtractPlugin 完成抽离css
// webpack.config.js
const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);
module: {
rules: [
{
test: /.scss$/,
use: [
// “style-loader”,
// 不再需要style-loader,⽤MiniCssExtractPlugin.loader
MiniCssExtractPlugin.loader,
“css-loader”, // 编译css
“postcss-loader”,
“sass-loader” // 编译scss
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: “css/[name]_[contenthash:6].css”,
chunkFilename: “[id].css”
})
]
==================================================================
1. 缩小loader处理范围
优化loader配置
-
test include exclude三个配置项来缩小loader的处理范围
-
推荐include
include: path.resolve(__dirname, “./src”),
2. resolve.modules
-
resolve.modules⽤于配置webpack去哪些目录下寻找第三⽅模块,默认是[‘node_modules’]
-
寻找第三方模块,默认是在当前项⽬录下的node_modules⾥⾯去找,如果没有找到,就会去上⼀级目录…/node_modules找,再没有会去…/…/node_modules中找,以此类推,和Node.js的模块寻找机制很类似。
-
如果我们的第三方模块都安装在了项目根⽬录下,就可以直接指明这个路径。
module.exports = {
resolve:{
modules: [path.resolve(__dirname, “./node_modules”)]
}
}
3. resolve.alias
resolve.alias配置通过别名来将原导⼊路径映射成⼀个新的导⼊路径
拿react为例,我们引⼊的react库,⼀般存在两套代码
-
cjs
-
采⽤用commonJS规范的模块化代码
-
umd
-
已经打包好的完整代码,没有采用模块化,可以直接执⾏
默认情况下,webpack会从⼊口文件./node_modules/bin/react/index
开始递归解析和处理依赖的⽂件。我们可以直接指定文件,避免这处的耗时。
alias: {
“@”: path.join(__dirname, “./pages”),
react: path.resolve(
__dirname, “./node_modules/react/umd/react.production.min.js”
),
“react-dom”: path.resolve(
__dirname, “./node_modules/react-dom/umd/react-dom.production.min.js”
)
}
4. resolve.extensions
resolve.extensions在导入语句没带文件后缀时,webpack会⾃动带上后缀后,去尝试查找⽂件是否存在。
默认值:
extensions:[‘.js’,‘.json’,‘.jsx’,‘.ts’]
-
后缀尝试列表尽量的⼩
-
导⼊语句尽量的带上后缀
5. externals
我们可以将⼀一些JS⽂文件存储在 CDN 上(减少 Webpack 打包出来的 js 体积),在 index.html 中通过 标签引⼊入,如:
我们希望在使用时,仍然可以通过 import 的⽅式去引⽤(如 import $ from ‘jquery’ ),并且希望 webpack 不不会对其进⾏打包,此时就可以配置 externals 。
//webpack.config.js
module.exports = {
//…
externals: {
//jquery通过script引⼊入之后,全局中即有了了 jQuery 变量量
‘jquery’: ‘jQuery’
}
}
6. CDN
CDN通过将资源部署到世界各地,使得⽤户可以就近访问资源,加快访问速度。要接⼊CDN,需要把⽹
⻚的静态资源上传到CDN服务上,在访问这些资源时,使⽤CDN服务提供的URL。
// webpack.config.js
output:{
publicPath: ‘//cdnURL.com’, //指定存放JS⽂文件的CDN地址
}
-
有cdn服务器地址
-
确保静态资源⽂件的上传与否
1. css压缩
-
借助 optimize-css-assets-webpack-plugin
-
借助 cssnano
安装
npm install cssnano -D
npm i optimize-css-assets-webpack-plugin -D
// webpack.config.js
const OptimizeCSSAssetsPlugin = require(“optimize-css-assets-webpack-plugin”);
new OptimizeCSSAssetsPlugin({
cssProcessor: require(“cssnano”), //引⼊cssnano配置压缩选项
cssProcessorOptions: {
discardComments: { removeAll: true }
}
})
2. html压缩
- 借助html-webpack-plugin
new htmlWebpackPlugin({
title: “my app”,
template: “./index.html”,
filename: “index.html”,
minify: {
// 压缩HTML⽂文件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空⽩符与换行符
minifyCSS: true // 压缩内联css
}
}),
3. 摇树(tree Shaking)
webpack2.x开始支持 tree shaking概念,顾名思义,“摇树”,清除⽆用 css,js(Dead Code)
Dead Code ⼀般具有以下⼏几个特征
-
代码不会被执⾏,不可到达
-
代码执⾏的结果不会被用到
-
代码只会影响死变量(只写不读)
-
Js tree shaking只支持ES module的引⼊方式
css tree shaking
npm i glob-all purify-css purifycss-webpack --save-dev
const PurifyCSS = require(‘purifycss-webpack’)
const glob = require(‘glob-all’)
plugins:[
// 清除⽆用 css
new PurifyCSS({
paths: glob.sync([
// 要做 CSS Tree Shaking 的路径⽂件
path.resolve(__dirname, ‘./src/*.html’), // 请注意,我们同样需要对html⽂件进行 tree shaking
path.resolve(__dirname, ‘./src/*.js’)
])
})
]
Js tree shaking
-
只支持import方式引入,不支持commonjs的方式引入
-
只要mode是production就会生效,develpoment的tree shaking是不生效的,因为webpack为了方便你的调试
-
可以查看打包后的代码注释以辨别是否⽣效
-
生产模式不需要配置,默认开启
// webpack.config.js
optmization: {
usedExports: true // 只要导出的模块被使用了,再做打包
}
副作用
// package.json
“sideEffects”:false //正常对所有模块进⾏行行tree shaking , 仅⽣生产模式有效,需要配合 usedExports 或者 在数组⾥里里⾯面排除不不需要tree shaking的模块
“sideEffects”:[‘*.css’,‘@babel/polyfill’]
4. 代码分割(code Splitting)
单页面应用spa
打包完后,所有页面只生成一个bundle.js
-
代码体积变大,不利于下载
-
没有合理利用浏览器资源
多页面应用mpa
如果多个页面引入了一些公共模块,那么可以把这些公共的模块抽离出来,单独打包。公共代码只需要下载一次就缓存起来,避免重复下载。
配置
其实code Splitting概念 与 webpack并没有直接的关系,只不过webpack中提供了一种更加方便的方法供我们实现代码分割,基于https://webpack.js.org/plugins/split-chunks-plugin/
optimization: {
splitChunks: {
chunks: ‘async’,// 对同步 initial,异步 async,所有的模块有效 all
minSize: 30000,// 最⼩尺寸,当模块大于30kb
maxSize: 0,// 对模块进行二次分割时使⽤用,不推荐使⽤
minChunks: 1,// 打包⽣成的chunk⽂件最少有几个chunk引⽤了这个模块
maxAsyncRequests: 5,// 最⼤异步请求数,默认5
maxInitialRequests: 3,// 最⼤初始化请求书,⼊口文件同步请求,默认3
automaticNameDelimiter: ‘-’,// 打包分割符号
name: true,// 打包后的名称,除了布尔值,还可以接收⼀个函数function
cacheGroups: {// 缓存组
vendors: {
test: /[\/]node_modules[\/]/,
name:“vendor”,// 要缓存的分隔出来的 chunk 名称
priority: -10// 缓存组优先级数字越大,优先级越⾼
},
other:{
chunks: “initial”,// 必须三选⼀: “initial”|“all”|“async”(默认)
test: /react|lodash/,// 正则规则验证,如果符合就提取 chunk,
name:“other”,
minSize: 30000,
minChunks: 1,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true// 可设置是否重⽤用该chunk
}
}
}
}
平时使用下面的配置即可
optimization:{
// 帮我们自动做代码分割
splitChunks:{
chunks:“all”,// 默认是⽀持异步,我们使用all
}
}
5. 作⽤域提升(Scope Hoisting)
作用域提升(Scope Hoisting)是指 webpack 通过 ES6 语法的静态分析,分析出模块之间的依赖关系,尽可能地把模块放到同一个函数中。下⾯通过代码示例来理理解:
// hello.js
export default ‘Hello, Webpack’;
// index.js
import str from ‘./hello.js’;
console.log(str);
打包后, hello.js 的内容和 index.js 会分开
通过配置 optimization.concatenateModules=true`:开启 Scope Hoisting
// webpack.config.js
module.exports = {
optimization: {
concatenateModules: true
}
};
我们发现hello.js内容和index.js的内容合并在一起了!所以通过 Scope Hoisting 的功能可以让 Webpack 打包出来的代码文件更小、运行的更快
6. 动态链接库(DllPlugins)
Dll动态链接库 其实就是做缓存
项目中引入了很多第三方库,这些库在很⻓的⼀段时间内,基本不会更新,打包的时候分开打包来提升打包速度,⽽DllPlugin动态链接库插件,其原理就是把⽹⻚依赖的基础模块抽离出来打包到dll
文件中, 当需要导入的模块存在于某个dll
中时,这个模块不再被打包,⽽是去dll
中获取。
-
动态链接库只需要被编译⼀次,项⽬中⽤到的第三方模块,很稳定,例如react,react-dom,只要没有升级的需求
-
webpack已经内置了对动态链接库的⽀持
-
DllPlugin:⽤于打包出⼀个单独的动态链接库文件
-
DllReferencePlugin:用于在主要的配置⽂件中引⼊DllPlugin插件打包好的动态链接库⽂件
新建webpack.dll.config.js⽂文件,打包基础模块
我们在 index.js 中使⽤了第三方库 react 、 react-dom ,接下来,我们先对这两个库先进行打包。
// webpack.dll.config.js
const path = require(“path”);
const { DllPlugin } = require(“webpack”);
module.exports = {
mode: “development”,
entry: {
react: [“react”, “react-dom”] //! node_modules?
},
output: {
path: path.resolve(__dirname, “./dll”),
filename: “[name].dll.js”,
library: “react”
},
plugins: [
new DllPlugin({
// manifest.json⽂文件的输出位置
path: path.join(__dirname, “./dll”, “[name]-manifest.json”),
// 定义打包的公共vendor⽂文件对外暴暴露露的函数名
name: “react”
})
]
}
在package.json中添加
“dev:dll”: “webpack --config ./build/webpack.dll.config.js”,
运行
npm run dev:dll
你会发现多了一个dll⽂文件夹,⾥边有dll.js文件,这样我们就把我们的React这些已经单独打包了
-
dll⽂件包含了大量模块的代码,这些模块被存放在⼀个数组里。⽤数组的索引号为ID,通过变量将⾃己暴露在全局中,就可以在window.xxx访问到其中的模块
-
Manifest.json 描述了与其对应的dll.js包含了哪些模块,以及ID和路径
打包好之后需要将dll文件注入index.html中,使用依赖add-asset-html-webpack-plugin
// webpack.config.js
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, ‘…/dll/react.dll.js’) // 对应的 dll ⽂件路径
})
运行项目
npm run dev
这个理解起来不费劲,操作起来很费劲。所幸,在Webpack5中已经不用它了,而是⽤ HardSourceWebpackPlugin ,⼀样的优化效果,但是使用却及其简单
-
提供中间缓存的作⽤
-
⾸次构建没有太大的变化
-
第⼆次构建时间就会有较大的节省
// webpack.config.js
const HardSourceWebpackPlugin = require(‘hard-source-webpack-plugin’)
const plugins = [
new HardSourceWebpackPlugin()
]
7. 并发任务(happypack)
运行在 Node.之上的Webpack是单线程模型的,也就是说Webpack需要⼀个一个地处理任务,不能同时处理多个任务。 Happy Pack 就能让Webpack做到这一点,它将任务分解给多个⼦进程去并发执行,⼦进程处理完后再将结果发送给主进程。从⽽发挥多核CPU电脑的威力。
// webpack.config.js
var happyThreadPool = HappyPack.ThreadPool({ size: 5 });
// module中添加
rules: [
{
test: /.jsx?$/,
exclude: /node_modules/,
use: [
{
loader: “happypack/loader?id=babel”
}
]
},
{
test: /.css$/,
include: path.resolve(__dirname, “./src”),
use: [“happypack/loader?id=css”]
},
]
//在plugins中增加
plugins:[
new HappyPack({
// ⽤唯一的标识符id,来代表当前的HappyPack是⽤来处理⼀类特定的文件
id:‘babel’,
// 如何处理理.js⽂文件,⽤用法和Loader配置中⼀一样
loaders:[‘babel-loader?cacheDirectory’],
threadPool: happyThreadPool,
}),
new HappyPack({
id: “css”,
loaders: [“style-loader”, “css-loader”]
}),
]
==================================================================
我们以实现一个简易的mini-webpack为目的,来串联一下webpack的打包原理。
- 首先定义一个webpack类
-
这个类需要有接收webpack.config.js这样的配置文件
-
这个类需要有一个执行构建的主方法,可以通过
new webpack(options).start()
开始构建
class webpack {
// 定义构造函数
constructor(options) {
// 获取配置文件中的入口和出口
const {entry, output} = options
this.entry = entry
this.output = output
this.modules = [] // 定义数组用来存放入口模块和其所有的依赖模块
}
// 开始构建的主方法
start() {}
}
- 解析入口文件,获取语法结构树AST
- 这里会涉及到
parser.parse
方法
const ast = parser.parse(entryInfo, {
sourceType: “module”
})
- 找到所有的依赖模块
-
这里会涉及到babel/core中的
traverse
方法 -
并且只需要找到type为
ImportDeclaration
的节点
traverse(ast, {
ImportDeclaration({ node }) {
}
})
- 将AST转化为code
- 将 AST 语法树转换为浏览器可执行代码,我们这里使用@babel/core中的
transformFromAst
和 @babel/preset-env。
// 提取内容,转化处理
const { code } = transformFromAst(ast, null, {
presets: [“@babel/preset-env”]
})
- 递归查看所有依赖项
- 递归查看的时候需要利用的技巧就是利用动态数组的长度遍历,一边push一边foreach
// 1. 获取入口文件
console.log(‘--------------1--------------’)
const entryInfo = this.parse(this.entry)
this.modules.push(entryInfo)
// 2. 递归分析其他模块
console.log(‘--------------2--------------’)
for(let i = 0; i < this.modules.length; i++) {
const item = this.modules[i]
const { denpendencies } = item // 获取依赖关系
if (denpendencies) {
for (let j in denpendencies) {
this.modules.push(this.parse(denpendencies[j]))
}
}
}
最后
全网独播-价值千万金融项目前端架构实战
从两道网易面试题-分析JavaScript底层机制
RESTful架构在Nodejs下的最佳实践
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
一线互联网企业如何初始化项目-做一个自己的vue-cli
思维无价,看我用Nodejs实现MVC
代码优雅的秘诀-用观察者模式深度解耦模块
前端高级实战,如何封装属于自己的JS库
VUE组件库级组件封装-高复用弹窗组件