一. Webpack
1. npm包
webpack
:核心包webpack-cli
:webpack命令行工具包-
2. 占位符
[name]
:模块名称[id]
:模块标识符[hash]
:模块标识符的hash[chunkhash]
:chunk内容的hash[query]
:模块的query,文件名?后面的内容
3. entry
4. output
文件输出output.clean
:替代clean-webpack-plugin
,在打包前清除dist文件夹
5. module
webpack模块,处理项目中不同类型的模块
rule.test
:匹配文件类型rule.loader
:设置单个解析器rule.use
:设置多个解析器,并可以添加options配置rule.exclude
:排除的文件夹;如babel-loader
排除/node_modules/
rule.generator.filename
:指定资源的输出路径和文件名module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset/resource',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024
}
},
generator: {
filename: 'assets/img/[contenthash:6][ext]'
}
}
]
},
rules.type
:设置类型用于匹配模块;有如下值处理不同类型的文件asset/resource
:发送一个单独的文件并导出URI,之前通过file-loader
实现。可以处理png、jpeg,gif等资源 ```javascript module: { rules: [ { test: /.png/, type: ‘asset/resource’ } ] }
// 所有的png文件都被发送到输出目录,其路径被注入到bundle中 import mainImage from ‘./images/main.png’; img.src = mainImage; // ‘/dist/151cfcfa1bd74779aadb.png’
- `asset/inline`:导出一个data uri,之前通过`url-loader实现`,可以处理svg等
```javascript
module: {
rules: [
{
test: /\.svg/,
type: 'asset/inline'
}
]
}
// 所有的svg文件都将作为data uri注入到bundle中
import metroMap from './images/metro.svg';
block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
asset
:webpack按照默认规则自动在resource、inline之间选择;默认小于8kb选择视为inline模块;否则视为resource模块。这里使用rule.parser.dataUrlCondition
配置多大的模块使用base64内置module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024
}
}
}
]
},
6. resolve
配置模块的路径如何解析
resolve.alias
:路径的别名,优先级高于其他规则;常用@表示srcresolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
7.devtool
配置如何生成source map
development环境推荐eval-cheap-module-source-map
production环境可以不添加这个选项不生成source map各种loader
1) vue-loader
webpack插件VueLoaderPlugin
:将定义过的其他规则复制到vue中;如/\.js$/
复制到vue文件的<script>
标签2) css-loader和style-loader
css-loader用于识别并加载css,style-loader用于将css转换为HTML中的style节点;css-loader importLoaders
:有@import
语法的时候,importLoaders决定@import
模块在使用css-loaders前使用多少个其他的loaders处理// importLoaders为2;说明@import模块在使用css-loader之前使用postcss-loader和sass-loader处理
module: {
rules: [
{
test: /\.sass$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}
]
}
3) sass-loader
支持解析sass,scss。
避免在@import
使用webpack中的alias(如@代表src),会识别失败
参考:webpack5 的使用(三):加载 css4) postcss-loader
将css解析成AST,通过其他插件做各种修改最终生成新的css
这是使用postcss添加浏览器前缀的方式
首先安装3个npm包
npm install -D postcss postcss-loader autoprefixer
在处理css,scss,sass的规则中添加postcss-loader
module: {
rules: [
{
test: /\.sass$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}
]
}
添加postcss.config.js和.browserslistrc
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
// 这是将px转化为vw的插件,不需要请忽略
{
'postcss-px-to-viewport': {
viewportWidth: 375
}
}
]
};
// .browserslistrc
> 0.2%
last 1 Chrome versions
not dead
各种plugin
1) html-webpack-plugin
当使用webpack打包的时候,创建一个HTML文件,并把打包后的静态文件自动插入到html文件中;注意需要设置favicon的路径,这样webpack运行的时候才能正确将favicon.ico放在dist目录下。
参考:webpack5 + vue3 从零配置项目2) clean-webpack-plugin
在打包前清除output配置的文件夹,webpack5中可以用
output.clean: true
替代3) webpack-bundle-analyzer
4) terser-webpack-plugin
5) mini-css-extract-plugin
提取css到单独的文件夹;建议从bundle提取css;需要同时设定module和plugin
module: {
rules: [
{
test: /\.css$/,
use: [
// 生产环境用MiniCssExtractPlugin.loader代理style-loader
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader'
]
},
{
test: /\.s[ca]ss/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
]
其他工具
1) webpack-merge
合并各个webpack配置 ```javascript const common = require(‘./webpack.common’); const { merge } = require(‘webpack-merge’);
module.exports = merge(common, { mode: ‘development’ });
<a name="Nxke3"></a>
### 二. babel
<a name="cIimG"></a>
#### 1) babel提供语法转换与polyfill功能
1. `@babel/core`:核心包
1. `babel-loader`:webpack中处理js的loader包
1. `@babel/preset-env`:babel预设环境,它会根据`browserlist`进行语法转换
1. `@vue/babel-preset-app`:vue的预设环境;
1. `@babel/plugin-transfrom-runtime`:编译时转译代码;所以用于开发环境
1. `@babel/runtime`:运行时转译代码,只能处理语法syntax;用于生产环境
1. `@babel/runtime-corejs3`:运行转译代码,可以处理语法,引入polyfill处理api;用于生产环境
<a name="smoGJ"></a>
#### 2) 不同的代码转换配置:
<a name="eI3Ll"></a>
##### 1. 不做任何配置:原样输出
<a name="p4Kc5"></a>
##### 2. `@Babel/preset-env`
1. 只转换syntax(class,typeof,箭头函数等),不转换api(map,include)
1. syntax的转换策略由browserslist而定
```javascript
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env'
]
]
}
// .browserslistrc
>0.25%
3. @Babel/preset-env
+ core-js@3
- 转换syntax和api
- syntax的转换策略由browserslist而定
- polyfill从core-js@3中引入
- useBuiltIns:是加载polyfill的策略
- usage,无需手动
import '@babel/polyfill'
根据browserslist策略 + 使用情况按需加上polyfill - false,不使用polyfill,如果手动
import '@babel/polyfill'
会无视策略全部加载polyfill - entry:需要手动
import '@babel/polyfill'
,根据浏览器策略加载polyfill,导致用不到的polyfill也会被加进去 ```javascript // babel.config.js module.exports = { presets: [ [ ‘@babel/preset-env’, { corejs: 3, useBuiltIns: ‘usage’ } ] ] }
- usage,无需手动
<a name="qcWLe"></a>
##### 4. 组合使用`@babel/preset-env`, `core-js@3`, `@babel/plugin-transform-runtime`, `@babel/runtime-corejs3`
4. core-js设置转译api,runtime设置false不转译api
4. runtime提取了helper代码,减少了代码重复
```json
{
"plugins": [
[
"@babel/plugin-transform-runtime", // 编译时开发环境转译
{
"corejs": false, // runtime不转译api,由下面的corejs转译
"helpers": true, // 提取helpers代码
"regenerator": false
}
]
],
"presets": [
[
"@babel/preset-env", // babel转换syntax
{
"corejs": 3, // corejs转译api,polyfill
"useBuiltIns": "usage" // 按需引入
}
]
]
}
三. eslint + prettier +vue
1. eslint
.eslintrc.js
配置选项的说明
root
:停止父目录寻找配置文件parserOptions.sourceType
:默认scirpt,如果是ES模块设为moduleparserOptions.parser
:默认Espree解析器;如果有babel可以使用@babel/eslint-parser
解析器env
:预定义的全局变量;可用的环境可以有browser,node,commonjs等globals
:添加自定义的全局变量名称extends
:扩展;值的说明printWidth
:允许的最长宽度semi
:是否加分号singleQuote
:是否使用单引号trailingComma
:是否在末尾追加逗号,可以设置为'none'
endOfLine
:windows和linux换行报错问题;可以设置为'auto'
3. eslint+prettier+vue3+vscode配置最佳实践
安装eslint,prettier,eslint-plugin-vue,eslint-config-prettier,@babel/eslint-parser
npm i -D eslint prettier eslint-plugin-vue eslint-config-prettier @babel/eslint-parser
各个插件的作用如下:
eslint-plugin-vue | 允许检查vue文件的<template> ,<script> 和js文件的vue 代码 |
---|---|
eslint-config-prettier | 关闭eslint中与prettier冲突的规则 |
eslint-plugin-prettier | 以eslint的方式运行prettier,并报错 |
@babel/eslint-parser | 允许检查所有合法的babel代码 |
- 添加.eslintrc.js文件,设置eslint ```javascript module.exports = { root: true, // 不从父文件夹检查eslint配置文件 parserOptions: { sourceType: ‘module’, // 默认script,如果是ES模块则是module parser: ‘@babel/eslint-parser’ // 允许所有合法的babel代码 }, env: { // 预设了哪些环境的全局变量 browser: true, node: true, es2021: true }, plugins: [‘prettier’], // 添加eslint-plugin-prettier插件 extends: [ ‘eslint:recommended’, // eslint推荐的配置 ‘plugin:vue/vue3-recommended’, // eslint-plugin-vue包内的vue3配置;vue2使用vue/essential ‘prettier’ // prettier配置 ], globals: { // 自定义全局变量 defineProps: true }, rules: { // 自定义规则 ‘prettier/prettier’: ‘error’ // prettier的规则报错 } };
3. 添加.prettierrc.js文件,设置prettier
```javascript
module.exports = {
printWidth: 100, // 一行文字长度的限制
semi: true, // 是否添加分号
singleQuote: true, // 是否使用单引号
trailingComma: 'none', // 是否追加逗号
endOfLine: 'auto' // 处理windows和linux的换行不一致
}
此时已经可以lint了,在package.json的scripts添加lint,fix,prettier,format等脚本
{
"scripts": {
"lint": "eslint --ext .js,.vue src",
"fix": "eslint --ext .js,.vue src --fix",
"prettier:fix": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,less,md,vue}",
"format": "npm run prettier:fix && npm run fix"
}
}
要在vscode中保存时自动lint;在
.vscode
文件夹中添加settings.json
```json { “editor.formatOnSave”: true, // 保存时format “editor.codeActionsOnSave”: { “source.fixAll.eslint”: true // 使用eslint来format }, “[vue]”: { “editor.defaultFormatter”: “esbenp.prettier-vscode” }, “[javascript]”: { “editor.defaultFormatter”: “esbenp.prettier-vscode” // 对vue和js使用prettier插件 } }
参考:[ESLint and Prettier with Vite and Vue.js 3](https://vueschool.io/articles/vuejs-tutorials/eslint-and-prettier-with-vite-and-vue-js-3/)<br /> [非vue/cli创建的vue2项目引入eslint和prettier最佳实践总结](https://juejin.cn/post/7073492841715990559)
<a name="jGHne"></a>
### 四. Typescript + Koa搭建
1. 安装typescript
```shell
npm i -D typescript
- 安装ts-node-dev,支持在开发中启动并更新node ```shell // shell npm i -D ts-node-dev
// package.json ts-node-dev app.ts
3. 开始安装typescript,koa依赖,koa,node的类型支持
```shell
npm i koa @types/koa typescript @types/node
初始化TyepScript配置文件tsconfig.json
tsc --init
在tsconfig.json中配置编译输出的目录
"outdir": "./dist"
为项目添加eslint,ts-eslint-parser和ts-eslint-plugin
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
在默认生成的.eslintrc.js中修改一下,这里eslitn使用的最基本的lint配置 ```javascript module.exports = { // 使用环境node env: {
node: true,
es2021: true
}, // 定义一个自己的ts解析器 parser: ‘@typescript-eslint/parser’, parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
// 告诉eslint typescript配置文件位置
project: ['./tsconfig.json']
},
plugins: [
'@typescript-eslint'
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking'
],
rules: { } }
参考:[ESLint x TypeScript](https://github.yanhaixiang.com/linter-guide/practice/eslint_typescript.html#%E5%86%8D%E6%97%A0-tslint)
7. 安装prettier,并集成到eslint中
```shell
npm i -D prettier eslint-config-prettier
// .prettierrc.js
module.exports = {
printWidth: 100,
semi: true,
singleQuote: true,
trailingComma: 'none',
endOfLine: 'auto'
}
// eslintrc.js
module.exports = {
root: true,
env: {
node: true,
es2021: true
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json']
},
plugins: [
'@typescript-eslint',
// 这是eslint-plugin-prettier, 让prettier以eslint的方式运行
'prettier'
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
// 使用eslint的拓展,'prettier/@typescript-eslint'的拓展已经被集成到这里
'prettier'
],
rules: {
// 配合plugin使用
'prettier/prettier': 'error'
}
}
- vscode保存时自动lint,在setting.json中
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2
}
}
五. mock数据的n种方法
1. 同源数据的mock
1) 侵入式的mock
不太推荐,把mock数据import进项目中,要判断运行环境。2) 非侵入式的mock
使用webpack的devServer.setupMiddlewares
,它为开发环境提供了自定义的中间件能力;接受一个函数,有middlewares和devServer两个参数;middlewares是个数组,请求按先后顺序在middlewares中处理。中间件是express风格的,因为就是基于express的。+ mockjs封装一下就可以mock啦
参考:webpack devServer setupMiddlewares{
devServer: {
setupMiddlewares: function(middlewares, devServer) {
devServer.app.get('hello', (req, res) => {
res.send('hi world')
})
middlewares.push({
name: 'first-middleware',
path: '/hi', // 请求的路径
middleware: (req, res) => {
res.send('hello world')
}
})
return middlewares
}
}
}
2. 跨域数据mock
跨域数据的mock没有什么找到什么好的资料。干脆直接使用koa+mockjs
直接起一个node服务。 ```javascript const Koa = require(‘koa’); const Router = require(‘koa-router’); const Mock = require(‘mockjs’);
const app = new Koa(); const router = new Router(); app.use(async (ctx, next) => { ctx.set(‘Access-Control-Allow-Origin’, ctx.headers.origin); ctx.set(‘Access-Control-Allow-Methods’, ‘GET, POST, OPTIONS, PUT, DELETE’); ctx.set(‘Access-Control-Allow-Headers’, ‘*’); ctx.set(‘Access-Control-Allow-Credentials’, true); next(); });
router.get(‘/hi’, (ctx) => { const data = Mock.mock({ description: ‘测试的数据’ }); ctx.body = data; });
app.use(router.routes()).use(router.allowedMethods());
const port = 9091;
app.listen(port, () => {
console.log(‘跨域服务器已启动…’);
console.log(端口${port}
);
});
<a name="lmPlc"></a>
### 六. 如何支持打包后动态修改配置
有时候打包后的包需要部署在不同的服务器中,需要支持修改配置文件,以便修改请求地址
<a name="z0XH2"></a>
#### 1. 在index.html模板中添加script
在index.html模板中添加script,注意最好插在头部,防止后面的脚本获取不到。将变量暴露挂载到window下
```html
<script src="./config.js"></script>
// config.js
window.config = {
BASE_URL: 'http://localhost:8080'
}
// 项目中直接获取变量
window.config.BASE_URL
可以把config.js放在项目static目录下,打包时在webpack中使用copy-webpack-plugin
将config文件原封不动的复制到dist根目录下。其实public/favicon.ico
文件同样可以复制过去。
2. axios请求拦截
使用请求的方式获取数据config.json; 这种方法较为繁琐,不会污染全局变量
- 首先创建一个axios实例,在请求拦截器中判断配置是否读取到。如果没有读取到则先请求获取数据。请求拦截器的第一个回调函数是支持异步的
- 如果数据没有缓存下来则会去请求获取config
- 严重问题,因为是异步的,所以在返回数据前所有的请求都会进入拦截请求一遍;
解决方案:添加一个tag位,保存const tag = axios.get('config.json')
,如果tag已经存在就不再请求
/** 请求拦截 */
service.interceptors.request.use(
async (config) => {
/** 请求配置文件,将BASE_URL存放在localStorage中 */
if (!localStorage.get('BASE_URL')) {
const response = await axios.get('config.json');
const global = response.data;
config.baseURL = global?.BASE_URL;
localStorage.setItem('BASE_URL', config.baseURL)
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
七. 如何修改IOS15 Safari顶部状态栏颜色
IOS15的Safari浏览器会根据html头部的meta信息修改状态栏颜色,如果没有会自适应使用background。
在vue中路由使用hash模式,更改background的时候并不会让Safari状态栏主动更新。需要手动设置更新
- vue的html模板中添加
<meta name="theme-color" content="#fff" />
- 使用vue不同的页面
const el = document.querySelector('meta[name="theme-color"]')
获取meta元素 设置content属性
el.setAttribute('content', 'red')
八. git
1. git回退版本
git reset --hard commit_id
回退到某个版本,这个版本之后的提交不保留git revert -n commit_id
删除某次提交,这个提交之后的提交保留2. tag和branch区别
tag是一个里程碑,静态的。可以为发布的里程碑打tag
branch是动态的,代码需要向前走九. 工程化项目CDN加速
配合webpack抽离出某些包使用cdn加速
在模板index.html中添加CDN链接,这里我们添加一个axios
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.1/axios.min.js"></script>
</body>
在webpack中添加externals,代表不需要将这个模块打包到最终的包中。
externals: {
axios: 'axios'
},
-
十. TSX+ Vue
自从升级到Vue3,Vue和Webpack就成了路人,Vue官方大力推广自家的vite。诶,vite是要学。
坑点1:@vue/eslint-config-prettier
不认prettierrc
,所以只能在eslintrc中设置module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier'
],
env: {
'vue/setup-compiler-macros': true
},
rules: {
'prettier/prettier': [
'warn',
{
printWidth: 100,
semi: true,
singleQuote: true,
trailingComma: 'none',
endOfLine: 'auto'
}
]
}
};