webpack功能
1. 配置全局变量
使用webpack自带的 providePlugin, 可以配置全局变量.
{
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: "jquery",
})
]
}
2. 多入口打包
多入口打包,就是打包的文件, 不止有index.html一个入口,而是多个. 比如login.html.那么就需要
- 在plugins中用
HtmlWebpackPlugin
创建多个入口 - 打包的js文件也不能打包在一个文件中, 一个入口的文件,不需要其他入口文件打代码.所以在entry里配置多个入口
- 在 HtmlWebpackPlugin的chunks里配置入口html文件要加载的js模块.
module.exports = {
mode: 'development',
entry: {
// 多入口打包
index: "./src/index.js",
login: "./src/login.js",
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html', // 最后再dist里生成的入口文件, 如果写 a.html的话, 在dist里就不是index.html, 而是a.html了
template: './src/index.html',
// 当前打包入口 加载的js模块, 这里的模块名是 entry里面的key对应的模块
chunks: ['index'],
}),
new HtmlWebpackPlugin({
filename: 'login.html',
template: './src/login.html',
chunks: ['login'],
}),
]
}
3. 图片打包
webpack自带文件处理插件. 用type: 'asset',
配置.
{
module: {
rules: [
{
test: /\.(png|jpg|jpeg|svg)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 1 * 1024,
}
},
generator: {
// 图片文件都 创建到 img文件夹下, 名称格式: 原名.6位hash.图片后缀.
// 带hash是为了防止不同文件夹下面有同名文件.现在放到 img文件夹下面了.为了区分加个hash
filename: 'img/[name].[hash:6][ext]',
}
}
]
},
}
4. 开发模式的本地服务器.
这里的文档我看了, 可以看懂
在有dev-server之前的开发情况
- 在源文件修改后, 执行npm run build, 打包生成dist打包文件.
- 在dist/index.html用"open with live server"打开, 查看修改内容
问题: - 每一次修改都得手动build一下, 才能看到, 非常麻烦.
解决方案: - 用webpack-dev-server开启一个服务器, 针对src里的源文件做监控, 当文件变化后, 自动build打包成dist. 然后用服务器打开dist/index.html.
webpack-dev-server
实际上是, 把开发代码,打包到dist,
然后在dist目录中, 以dist为根目录,启动服务器, 打开了index.html文件.
实际效果跟在vscode中, 右键点击dist/index.html
,选择 open with live server
是一样的.
所谓的开发模式, 就是
- 以dist为根目录,启动服务器, 打开dist/index.html文件.
- 当源码文件有变化时,就打包一次. 更新dist里的文件代码
- hot热更新模式,就是当监控到文件变化时, 主动更新浏览器.刷新浏览器,或dom结构.
- 关于图片文件.在源码中路径是
./img/login/icon.png
,由于打包后统一放到了dist/img
下面, 所以应该是看不到图片的,此时图片的真实地址是http://localhost:9080/img/19.77aff4.jpg
- 为了解决问题4, 使用了
CopyWebpackPlugin
,该插件会把图片的完整路径,复制一份到dist
下面,
这样dev-server就能找打对应的文件了. 同时这份文件只是为了开发, 所以只存在于内存中. 实际在dist下面看不到. - 到最后,不是开发命令而是 在执行打包命令时, 因为实际的图片都打包在
dist/img
里, 所以后面会在打包时,把所有的引入img的路径,改成从dist/img
里面拿图片.
备注:
- copy-webpack-plugin的文档 https://www.webpackjs.com/plugins/copy-webpack-plugin/
- dev-server的文档 https://www.webpackjs.com/configuration/dev-server/#devserver
{
devServe:{
// 打包文件路径
static: './dist',
server: 'http', // 启动的服务式http
port: 9080, // 端口好
https: false, // 是否使用https
hot: true, // 热更新
open: true, // 是否自动打开浏览器
proxy: { // 代理
'/api': 'http://localhost:3000',
},
watchFiles: ['src/**/*.php', 'public/**/*'], // 监听文件变化范围.
webSocketServer: 'ws', //使用web-socket服务器
},
plugins: [
// 复制开发文件到, 打包文件的插件.
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, './src/img'),
to: path.resolve(__dirname, './dist/img'),
}
]
})
]
}
5. css抽离
使用style-loader, css-loader
的问题
- 当前对css的处理是在用
css-loader
获取到css文件后, 用style-loader
,创建一个函数, 函数效果是 直接在html上插入一个style标签. 把css内容写入这个style标签里. - 这个函数会被打包到index.js里, 在index.html启动后执行.
- 这样的话, 所有的css文件都会在index.js里.会使index.js过于膨胀.也不方便对css进行压缩. 不符合我们对js分门别类的需求.
- 所以我们打算把所有的css文件都打包到一个css文件夹中.
使用MiniCssExtractPlugin
把css单独打包. 把css从index.js文件冲抽离出来.
- npm install --save-dev mini-css-extract-plugin
- 使用miniCss自带的
MiniCssExtractPlugin.loader
,代替style-loader获取css内容 - 在plugins里配置.css文件打包的路径.
效果:
未抽离css时, login.js为: 9.2kb. 抽离后变成了 login.js: 3.1kb, login.css: 5.9kb.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
// 使用minicss的loader获取到css的内容chunk
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [
// 把css内容打包到 dist/css下.
new MiniCssExtractPlugin({
filename: 'css/[name].[hash:6].css',
chunkFilename: 'css/[name].css',
})
],
};
备注: https://www.webpackjs.com/plugins/mini-css-extract-plugin/
6. js代码压缩
问题: 打包后的js文件太大.
解决方案: js压缩
npm install terser-webpack-plugin --save-dev
treeshaking, lodash-es替换lodash
7. css,js代码压缩
问题: 打包后的文件太大.
解决方案:
- css压缩, 使用
CssMinimizerWebpackPlugin
压缩css, - 使用
terser-webpack-plugin
压缩js. 注意视频里说用uglifyjs-webpack-plugin
,但是这个插件式webpack4版本的, webpack5已经没有了, 应该是版本更新去掉了. 到webpack的插件列表里找,找到了替代品terser-webpack-plugin
.
const MiniCssExtractPlugin = require("mini-css-extract-plugin") // css解析插件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // css压缩插件
{
// 优化配置项
optimization: {
minimize: true, // 是否压缩
minimizer: [
//js压缩插件
new TerserPlugin({}),
//css压缩插件
new CssMinimizerPlugin(),
]
},
module: {
rules: [
{
test: /\.css$/,
use: [
// css解析器用 MiniCssExtractPlugin.loader
MiniCssExtractPlugin.loader,
'css-loader',
]
},
]
}
}
8. treeshaking 不打包未使用的函数
tree,树, shaking摇动, treeshaking摇动树木, 把不用的叶子摇晃下来. 意为打包时不打包没有使用过的函数代码
以lodash为例
- 比如只使用了
lodash
里面的deepClone
函数, 但却打包了lodash的所有的代码. 这肯定是不划算的. 如果是把deepClone
打包进入,不打包lodash
的其他代码.就很好的压缩了文件大小.这就是treeshaking. - 不加lodash,打包后的index.js有
167kb
- 使用
import _ from 'lodash'
的方式使用_.get的index.js有279kb
- 使用treeShaking方式使用get函数的index.js有
170kb
,可见treeShaking有多么必要
使用treeshaking有几个条件
- 用解构方式获取函数
import { get } from 'lodash'
lodash
必须符合esModule规范. 也就是用如下方式导出函数. 不能用module.export
.
export function get() {
...
}
// 这是lodash的lodash.js的函数导出方式. 定义了一个lodash对象. 然后在lodash对象上加各种函数,再把lodash对象导出.
lodash.add = add;
lodash.attempt = attempt;
lodash.camelCase = camelCase;
lodash.capitalize = capitalize;
lodash.ceil = ceil;
lodash.clamp = clamp;
lodash.clone = clone;
lodash.cloneDeep = cloneDeep;
lodash
不符合esModule规范,所以treeShaking不生效. 要使用符合esModule规范的lodash-es
才行.
import { get } from 'lodash-es';
- 即使函数都在同一个文件,也可以被treeshaking去掉, 条件是 webpack.config.js里的配置的mode改成生产模式.
mode=production
- 一定要用解构赋值来获取函数, 这样就可以保证工具函数是esModule的写法(因为导出对象的方式没法用解构来获取函数.),
export function get(){}
import { get } from 'tools';
// lodash-es的lodash.js文件的导出方式., 直接用export把各个函数导出.
export { default as add } from './add.js';
export { default as after } from './after.js';
export { default as ary } from './ary.js';
export { default as assign } from './assign.js';
export { default as assignIn } from './assignIn.js';
export { default as assignInWith } from './assignInWith.js';
9. splitChunk 打包文件 index.js的分割
目前, 所有的js文件, 包括自己的和node_module的,全都打包在了一个文件index.js中.
包括: jquery, lodash, jquery-flexslider, 一共1.6M.这就很大. 所以现在我们要分割这个文件
注意: 通俗的讲,"chunks" 是指交给webpack处理的文件,例如:js/css/img文件,node_module里导入的插件. 通过import的方式引入的模块或文件都可以称之为"chunks"。
-
把自己项目的业务js文件和node_module的插件文件分离. 因为node_module里是插件, 一个模块不会依赖其他模块. 相对好分割. 但是自己的业务文件. 由于有依赖关系不好分割.
所以 先把业务文件和插件文件分割. -
分割是有最小粒度的, 每一个被
import
导入的资源,库,组件都是一个chunk. 只能是不同chunk之间进行组合.一个chunk不能分割成两个文件. -
optimization.splitChunks
配置分割方式. 这里的配置项更像是分析配置. 简单来说就是先打包成一个文件index.js. 然后, 如果某些chunk单独打包出来符合配置的条件.就会把他单独打包出来.否则就合并到同一的index.js里-
chunks: 要分析的类型.
initial
就是从webpack.config.js
的entry
属性里多入口分离的文件.也就是业务组件. 比如
{ entry: { index: "./src/index.js", login: "./src/login.js", }, }
splitChunks
配置的属性就会对业务模块的代码进行分割async
应该是从node_module里导入的模块的分割(不确定).all
将业务代码和node_module
里的代码进行分割. 就是index.js里只有业务代码.node_module
里的工具代码单独打包一个文件(或多个文件).- chunks的类型的效果受minSize影响. 达到minsize大小了,才会单独作为一个包. 没有达到则还是都在index.js里.
-
minSize: 10 * 1024, 单位bite, 10 * 1024就是10kb. 这里的
minSize
是如果某个chunk打包后有10kb则会被单独打包, 否则就放到index.js里,哪怕index.js已经很大了.也是如此. 也就是说大文件单独作为一个chunk, 小文件都打包到index.js里. -
name: 打包后的文件名,可以是函数. 如果是多入口项目(比如login, index). 目前name的函数里没有entry, 无法区分具体是哪个入口. 所以可以直接用默认的名字.
- 默认名称是由打包的文件名拼接而成的.比如里面有juqry和lodash, 那名字里就是
vendors-node_modules_jquery_-node_modules_lodash_lodash_js.540af9.js
- 如果login和index拆出来的splitChunks文件的构成一样.比如都是juqry和lodash, 那么名字就是会一样. 但是没关系,因为里面内容也一样.
- 默认名称是由打包的文件名拼接而成的.比如里面有juqry和lodash, 那名字里就是
-
cacheGroups: 分组打包, 单独打包一个jquery, test模块名匹配,name打包后的名称.chunks类型.
jquery: { test: /jquery\.js/, name: 'jquery', chunks: 'all', },
-
一般来说minSize可以设为300kb, 注意这里的300kb是在生产环境下压缩后的文件大小,不是开发环境的文件大小.
-
{
optimization: {
splitChunks: {
chunks: 'all',
minSize: 1 * 1024,
name: 'common',
cacheGroups: {
jquery: {
test: /jquery\.js/,
name: 'jquery',
chunks: 'all',
},
'lodash-es': {
name: 'lodash-es',
test: /lodash-es/,
chunks: 'all',
}
}
},
},
}
10. ejs 原始项目的公共模板
- 下载ejs, ejs-loader
npm i ejs
npm i ejs-loader -D
- 配置ejs-loader
module: {
rules: [
{
test: /\.ejs$/i,
loader: 'ejs-loader',
options: {
esModule: false,
}
}
]
},
- 创建ejs文件
创建src/ejs/header.ejs
文件
<p class="fl">
<a href="login.html" id="login">登录</a>
<a href="#" id="reg">注册</a>
</p>
- 使用ejs文件
<body>
<!-- 函数参数里可以传递数据 -->
<%= require('./ejs/header.ejs')() %>
</body>
11. clean 每次打包后都清空dist
在webpack5, clean-webpack-plugin
已经被集成到了webpack里,成了默认插件了. 可以直接用webpack.config.js
里的配置控制
module.exports = {
//...
output: {
clean: true, // 在生成文件之前清空 output 目录
},
};
12. copy-webpack-plugin, 将源文件中的某些文件直接复制到dist包中.
适用范围:
- 比如说有些静态文件. 并不被html文件引用. 而是可以直接在浏览器查看, 单独占一个地址. 比如一些pdf文件(同意协议, 法律条文),
- 这些文件由于不被html引用, 所以不会被webpack处理, 也就不会直接出现在dist目录中.
- 又不能每次都手动复制过去, 所以有了
copy-webpack-plugin
, 帮我们把这些文件直接复制到dist里面. - 配置:
const CopyPlugin = require("copy-webpack-plugin");
{
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(rootPath, "./src/static"),
to: path.resolve(rootPath, './dist/txt'),
}
]
})
]
}
13. vue-loader, @vue/compiler-sfc , vue-template-compiler
- vue-loader: 识别
.vue
文件, 可以将.vue
文件里的html, css, js识别出来. vue-loader@15版本以上的可以识别vue3代码 - @vue/compiler-sfc: 将vue-loader识别出来的html, css,js进行进一步处理, 生成vue组件.
- vue-template-compiler: 专门针对
<template>
标签进行处理. @vue/compiler-sfc可以对template,script, style所有标签都处理,但是没有vue-template-compiler专精. - 在webpack配置中, 只用配置vue-loader, 另外两个 @vue/compiler-sfc , vue-template-compiler是vue-loader调用的, 只要存在就可以了.
vue-loader的使用
// 跟vue-loader配套使用的插件
const { VueLoaderPlugin } = require('vue-loader')
// webpack的配置
{
module: {
rules: [
{
// 此处没有ts的适配.只是识别vue文件. ts以后再说
test: /\.vue$/,
loader: 'vue-loader',
}
]
},
plugins: [
// vue-loader的配套插件
new VueLoaderPlugin(),
]
}
14. devtools配置sourceMap
在浏览器看源代码
devtools: string
- eval: 只展示打包后的代码, 不展示源码
- "eval-source-map": 展示源代码. 此处没有管ts. ts的解析另说.
- 要在浏览器上看到自己的业务源代码, 最好是通过控制台console里的log,点击对应文件查看. 直接看source分不清哪个是哪个.
15. vue-router
- npm install vue-router@3 // npm install vue-router 默认的是vue-router@4, 4对应的是vue3, 不能用于vue2
- router.js
import Home from './page/home.vue';
import VueRouter from 'vue-router';
import Vue from 'vue'
/*vue.use的用处
1. 创建router-link, router-view标签.
2. 注册 this.$router, this.$route 对象.
*/
Vue.use(VueRouter);
// 路由配置
const routes = [
{
path: "/",
redirect: 'home',
},
{
path: "/home",
name: 'home',
component: Home,
}
]
// 创建router实例.配置其他参数.
const Router = new VueRouter({
routes,
})
export default Router;
- 在main.js里注册router
import Vue from 'vue';
import App from "./App.vue";
import router from './router.js';
new Vue({
router,
render: h=> h(App)
}).$mount("#app");
- 在App.vue里使用router-view
<template>
<router-view></router-view>
</template>
16. mpa改造
当前的单页面应用只有index.html一个入口. 不利于搜索引擎识别页面内容. 对于页面在搜索引擎的排名不利. 为了对项目进行seo优化.
我们把项目改成多页面应用, 每一个页面都在dist里有一个对应的入口html文件.比如login.html, home.html
核心原则就是: 把每一个页面都当成一个项目去处理
- 每一个页面都有一个入口文件, 比如"src/mpa/home.js", "src/mpa/login.js"
import Vue from 'vue';
import home from "../page/home.vue";
new Vue({
// 这里的home本来是app.vue但是现在由于只展示home组件,所以可以直接放home, 不再需要router.
render: h=> h(home)
}).$mount("#app");
- 新建
webpack.vue.mpa.config.js
. 进行mpa的项目配置- entry改成多个
entry: {
// 'index': path.resolve(rootPath, "./src/main.js"),
'home': path.resolve(rootPath, "./src/mpa/home.js"),
'login': path.resolve(rootPath, "./src/mpa/login.js"),
},
2. HtmlWebpackPlugin配置多个, 在dist里生成多个html文件
// 用index.html模板在dist下生成一个html文件
new HtmlWebpackPlugin({
filename: 'login.html', // 最后生成的login的入口文件
template: path.resolve(rootPath, "./public/index.html"), // 模板用统一的模板
// 当前打包入口 加载的js模块, 这里的模块名是 entry里面的key对应的模块
chunks: ['login'],
}),
- 修改package.json里的script,添加mpa命令
"scripts": {
"start:mpa": "webpack-dev-server --config ./build/webpack.vue.mpa.config.js",
"build:mpa": "webpack --config ./build/webpack.vue.mpa.config.js"
},
vue3改造
vue3比vue2更快, 更好用. 打包后体积更小, 反应更快. 并且vue2已经不在被支持,所以我们的vue2项目要升级到vue3.
- vue3: 3.4.38, vue-router: 4.4.3, vue-loader: 16.8.3, 在package.json里配置
"vue": "^3.0.0",
就会自己去找3版本的最新版本. - vue-router的创建
import Home from './page/home.vue';
//createRouter创建router对象, createMemoryHistory创建router的历史记录模式
import {createRouter, createMemoryHistory} from 'vue-router';
const routes = [
{
path: "/home",
name: 'home',
component: Home,
},
];
const router = createRouter({
history: createMemoryHistory(),
routes,
})
export default router;
- vue的创建. main.js
import router from './router.js';
import App from "./App.vue";
import {createApp} from 'vue'
const app = createApp(App).use(router).mount("#app");
- vue3的语法和router的使用
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
const goLogin = () => {
router.push('/login');
};
const message = ref('li');
onMounted(() => {
message.value = '李飞';
});
return {
message,
goLogin,
};
},
};
英语
- generator: 发电机, 发生器. 引申为创建含义, 创建文件的配置
- provider: 提供者, 供应者, 在webpack里是, 遇到未定义的变量 提供引入模块.
- optimize: 使...优化. optimization优化项. 使用webpack对打包的优化配置项.
- exclude: 排除, 不是用优化程序的文件目录.
- treeshaking: tree,树, shaking摇动, treeshaking摇动树木, 把不用的叶子摇晃下来. 意为打包时不打包没有使用过的函数代码
每个技术要搞懂的内容.
- 出现这个技术之前的现状是什么.
- 这个现状有什么问题, 这个技术是为了解决什么问题而出现的.
- 解决方案是什么.
- 解决后比原来好在哪里(选)
遇到的问题
-
devServer配置不生效问题.
问题:- 在webpack.config.js里配置了
webpack-dev-server
. port设为9000. - 在package.json的script里配置了命令
start: webpack-dev-server
. - 执行npm start后. port是8080.
原因:
- 在
webpack-dev-server
命令中, 默认的webpack.config.js的位置实在根目录, 而我的是在'build'文件夹下. devserver没有找到配置文件,所以用的默认配置.所以port是8080. "build"命令配置了配置文件位置, 所以没有出问题
解决方案:
- 修改start命令为:
"start": "webpack-dev-server --config ./build/webpack.config.js",
- 在webpack.config.js里配置了
性能优化
1. 多入口打包, js入口的分离.
每个入口只打包他们自己的文件, 而不是全部js文件. 详见 `9. splitChunk 打包文件 index.js的分割`;
2. 入口文件优化.
index.js文件过大. 把index.js分割成多个大小适当的文件. 比如用`mini-css-extract-plugin`单独把css文件抽离出来(style-loader是把css内容直接打包到index.js里的.)
3. 代码压缩
js压缩: terser-webpack-plugin , css代码压缩: css-minimizer-webpack-plugin;
4. treeshaking
未被调用的函数不打包进dist里. 比如用lodash里的工具函数, 用哪个就打包哪个,而不是整个打包进去. 常用的就那几个.
使用别的工具函数的时候,一定要用解构赋值来获取, 防止引入不必要的函数.
5. splitChunks
针对所有js文件打包成的index.js进行分割. 比如首屏优化. 在单页面应用中, 进入页面时是会把所有的公共js文件都加载的. 但是首页可能只用到很少的公共js库.
这时可以把 "首页的业务文件", "首页用到的公共js文件"单独拉出来. 首页用不到的公共文件单独放一个文件.这样当进入页面时,首页的业务文件, 首页用的公共js文件加载完后页面就展示了. 首页用不到的公共文件还未返回时,不影响页面展示. 可以在后面慢慢请求.
export function get(){}
import { get } from 'tools';
标签:文件,功能,vue,js,webpack,打包,css
From: https://www.cnblogs.com/bridge7839/p/18572991