前言
之前对webpack并没有过多的认识,只是跟着教程一步一步设置,再加上时间久了就完了,所以现在打算再复习一下,对其中基础常用的功能加深印象,顺便记录下来,当作自己以后翻阅的工具贴
webpack 是什么
目前一般项目都用webpack进行打包,由于前端各种模块化不通用,浏览器又不能识别那些语法,通过webpack可以进行转化,jsx、ts这些都可以,很方便,比如 react/vue 项目的脚手架,目前都是基于webpack来构建的
首先 , webpack 是依赖 node 环境的,所以必须要有 node,其次也暗示了其配置文件webpack.config.js的导入和导出应该使用Common JS的方式。
全局安装 webpack
简单试水一下,所以直接全局安装,然后直接针对某个文件夹里的文件进行打包,需要安装webpack 和 webpack-cli (webpack-cli是为了能在命令行里通过指令直接运行webpack)
因此直接npm install webpack webpack-cli -g ,然后就可以在一个文件夹里面试着运行了(具体是xx/src/xxx的文件目录形式,然后在xx下运行webpack,可以打包src里面的文件到xx/dist里)
比如,我在下面的文件里,index.js导入了js文件夹下的两个文件,分别是ES module和Common JS的方式,具体就不写出来了,知道意思就行:运行 node index 是不能正常运行的,会报错 SyntaxError: Cannot use import statement outside a module
文件目录如上所示,如果不是src下面的文件,直接命令行里面,敲webpack,运行全局的webpack,会报错
所以,虽然没有看源码,但是推断如果全局用的话,必须是要在src里写,然后默认打包src里的文件,输出到外面的dist文件夹下
然后运行webpack,就可以得到打包后的文件了,此处运行node main.js就能成功运行了。
局部安装 webpack
为什么有了全局还要安装局部的,因为多个项目可能依赖的不是同一个版本的webpack,全局的webpack只有一个版本,所以需要针对不同的项目安装不同版本的webpack。
在文件夹里,用npm init -y生成package.json后,有package.json的后就相当于是一个可以进行包管理的小项目了,就可以进行局部安装了,比如npm install webpack -D,这个时候要区分是生产环境还是开发环境
npm install -g/D/S
这里复习一下局部安装的
-S和-D; 参考
devDependencies与dependencies的区别,点击跳转参考链接
devDependencies里面的插件只用于开发环境,不用于生产环境,比如webpackdependencies是需要发布到生产环境的(开发环境也得用,相对于全程都需要),比如vue、antd
一开始我也分不清,觉得写在package.json里的开发环境和生产环境的依赖,只跟最后webpack打包有关系吗,node跟webpack最开始也没啥关系嘛,那一开始node搞的这个两种环境有啥作用哦?
在没有webpack的时候,package.json里面照样还是有区分devDependencies与dependencies,主要是两种情况:
- 发布到GitHub上的源码,其他开发者直接git clone下来,这个时候,是有着完整的package.json的,所以别人也是能完整的看到你的devDependencies与dependencies,当他们
npm install的时候,不管在哪上面的依赖都是会全部下载到node_modules下,就跟你自己开发的时候一模一样; - 发布到npm上,别人就是通过
npm install module_name的形式,下载你放上去的包,这个时候,只会下载你在dependencies里面的依赖 - 如果是在npm上下载你的项目,但是你写错了依赖,把vue写到devDependencies,那么别人安装的时候,就不会安装vue,就会提示错误,vue找不到;如果你把jest测试用的依赖写到dependencies里,别人下载安装就会把这个也安装上,别人的安装包就很臃肿,因为他们根本用不到jest
疑问,如下
- 这两者的区别,在很多文章中都是说在项目打包/上线的时候,devDependencies的包不会打包进去,而一说到打包/上线我就很自然而然的想到webpack来打包一个web项目,然后部署上线,用户能用浏览器进行访问,然而其中的文章,又说像webpack之类的依赖,就不需要打包到生产环境里面,我就比较迷惑。
然后看到了这个回答(下面分割线),我想了一下,或许很多帖子也是互相借鉴,其中说的打包/上线,应该就是指的nodejs应用,也就是写了一个类似axios、vue这种项目,通过发布到npm上面,意思就是上线了这个项目,大家都可以去下载,这种项目可能也是经过webpack进行打包,只不过最后上线到npm上
然而如果说特指webpack来进行打包上线一个web项目,靠的webpack的逻辑,比如css-loader这些,安装在dev里面,而vue这些安装在dependence里面,通过webpack的逻辑判断哪些需要进行转换到es5,最后进行压缩打包,出来的就是一个包含了能完整运行项目逻辑的代码,不管是写在dependence还是devdependence里的依赖,只要相关的部分,都打包进来,这个“打包上线”到服务器似乎和刚刚那种项目打包上线到npm不太一样,打包环节通过webpack都是一样的打包,只不过最后一个是放到自己服务器上,比如直接就是能访问的网页了,不会再让其他开发者来接着用了,我这里称为项目,之前那个则是发到npm上,开发者可以继续下载,在此基础上用来开发自己的其他项目,我这里称为依赖。
说到底devDependencies与dependencies这俩的出现,是为了让开发者在通过npm install xxx的时候,只下载别人配置好的生产依赖就行,不必要把人家的开发依赖也下载,用不上,浪费空间。不要过多联想,我把自己都绕懵了。。。
有如下命令:(其中install可以直接简写成i)
-
npm install不带任何后缀的时候,表示本地安装,一般是项目初始化的时候,就直接在该项目文件夹下,把package.json里的module安装到./node_modules下,通过require()来引入。 -
npm install module_name -gglobal 的意思,全局安装,node和刚刚安装的webpack都是全局安装的,在哪都可以直接在命令行里用,module 的包放在/usr/local下或者你node的安装目录。 -
npm install module_name -S或者npm install module_name --save
-S 是 --save 的简写,表示安装到生产环境下,表明这个依赖从开发到上线(生产)都需要使用,会在dependencies里记录,不能小写-s
-
npm install module_name不带后缀的时候,默认是--save,表示安装到生产环境下 -
npm install module_name -D或者npm install module_name --save-dev-D 是 --save-dev 的简写,表示安装到开发环境下,上线后不需要,因此不需要打包,会在devDependencies里记录,不能小写-d
回到上节内容
之后在命令行中执行npm webpack 依旧是用的全局的webpack,需要直接终端里执行./node_modules/.bin/webpack才是执行的局部安装的webpack,或者执行npx webpack (本地安装后执行) ,但是这样依旧不是很常用,npx 命令是用于执行node_module/.bin/目录下的shell脚本的命令
注意要安装webpack-cli
但是这样并不常用,所以一般是在package.json里的scripts里面配置webpack后再直接运行脚本,比如下图这样,然后直接运行npm run build
在命令行中执行 npm run build ,这样看起来比npx webpack还多写了一些单词,但是看截图里的 “test” 就知道,当命令很长的时候,就可以直接少写那一堆,用一个test代替
webpack 的配置
webpack的配置一般都很多,为了防止在package.json中写的script脚本过长,所以有一个文件来专门配置webpack,然后通过Common JS 的 module.exports = { } 导出该对象
webpack是通过node环境来运行的,所以它的配置文件应该用Common JS的语法来进行导出
配置文件的名字不能随便写,通常是webpack.config.js,而且这个导出了之后,不用自己去package.json里面导入啥的,webpack会自动在根目录里找到并读取,所以名字不能乱取
如果非要改名字,或者有不同的配置,比如生产环境和开发环境下,需要不同的脚本运行,就需要自己在package.json里单独配置了,具体如"build-custom":"webpack --config voiceu.js"
webpack.config.js如下:
const path = require('path')
module.exports = {
entry: "./src/main.js",
output: {
// path必须是绝对路径,可以在命令行中输入 pwd 来获取,或者用 path模块
path: path.resolve(__dirname, "./build"),
filename: "bundle.js"
}
}
在里面直接导出,在package.json里面只需要配置webpack的脚本就行,会自动识别这些配置,不需要再导入之类的
下面介绍一些常用的配置
entry
入口文件,默认是 index.js ,可以自己修改,比如上面的代码就改成了 main.js,好理解,不多说
output
输出文件,可选项有 path 、 filename 等,看名字就知道啥意思,不过path需要是绝对路径,path必须是绝对路径,可以在命令行中输入 pwd 来获取,或者用 path模块来,在上面代码里都有写。
loader(重要)
名字就说清楚了,加载器,类似css这种,webpack并不能直接像识别js一样识别出来,需要一些loader来处理
一般有三种方式1、内联(比较少见);2、cli(webpack5就不再使用);3、配置文件
在配置文件里面是写在module对象里的rules数组里面,test正则匹配文件后缀,loader有顺序,use里面是倒序
module: {
rules: [
{
test: /\.css$/, //正则表达式,表明文件后缀以.css结尾的就使用这条rule
// loader 的语法糖
// loader: "css-loader", //这是字符串
// 完整写法,因为有可能是多个loader进行按序配合,所以是数组,顺序是倒序
use: [
// 简便写法直接写这个"css-loader"
// "css-loader",
// 完整写法,里面还可以有其他配置,这里没写出来
// { loader: "css-loader" }
// 注意是倒序,先执行最后的
"style-loader",
"css-loader",
"postcss-loader"
// {
// loader:"postcss-loader",
// options:{
// postcssOptions:{
// plugins:[
// require('autoprefixer')
// ]
// }
// }
// }
]
},
}
常用的有
- style-loader
把css写入style中,页面才能正常显示
- css-loader
加载css
- less-loader
需要搭配less,所以还要安装less
- file-loader
-
url-loader 可转base64编码,同样会出现file-loader的问题,解决方法可见上方链接
-
vue-loader 用来加载.vue文件的
-
babel-loader babel可以有单独的配置文件
babel.config.js如果不在use下的options里面配置,就可以直接在配置文件里面写,
{
test:/\.js$/,
use:[
{
loader:"babel-loader",
options:{
// plugins:[
// "@babel/plugin-transform-arrow-functions",
// "@babel/plugin-transform-block-scoping",
// ]
//可以通过babel.config.js来进行导入
// presets:[
// "@babel/preset-env"
// ]
}
}
]
}
plugin(重要)
插件,做loader做不了的功能,很重要
在最上面,需要导入各个plugin的类,而且形式不一样,有的是解构,有的不是
// loader不用自己导入,直接在module里写就行,但是plugin需要手动导入
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require("html-webpack-plugin")
// DefinePlugin 是内置的plugin,不需要单独安装
const { DefinePlugin } = require('webpack')
const CopyWebpackPlugin = require("copy-webpack-plugin")
const { VueLoaderPlugin } = require("vue-loader/dist/index")
module.exports = {
// plugins是数组,里面是对象,但是在引入的时候是类,所以需要创建新的对象
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "voiceu",
template: "./public/index.html",
}),
new DefinePlugin({
BASE_URL: "'./'",
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
}),
new CopyWebpackPlugin({
patterns: [
{
from: "public",
to: "./",
globOptions: {
ignore: [
"**/index.html"
]
}
}
]
}),
new VueLoaderPlugin()
]
}
常用的有
-
CleanWebpackPlugin 每次打包之前,清空上次的打包文件
-
HtmlWebpackPlugin 生成一个html入口,可以指定模板
-
DefinePlugin webpack自带,不需额外下载,用于定义一些常量
-
CopyWebpackPlugin 把静态资源,复制到打包文件夹下,比如public下的favicon.icon
-
VueLoaderPlugin 用来加载.vue文件的
mode
- mode: "development",
- mode: "production",
devtool
默认是eval,教程说不利于查错,所以可以写成source-map,但实际上测试的时候发现eval才能映射到具体文件,可能版本原因
- devtool: "eval",
- devtool: "source-map",
watch
监听文件变化,只要发生了改变,就会自动重新编译,但是浏览器不会刷新,有两种方式
- 在
package.json里面对应的脚本后加上--watch - 配置中,设置为
true
devServer
安装 npm i webpack-dev-server -D 后,可以在package.json里面的脚本中添加"serve" : "webpack serve" 进行开启服务器,一般在开发阶段使用,这样就可以不用watch了,这个相对于也是通过写一个serve的配置选项,告诉终端运行某shell脚本,webpack会自动根据这个serve来选择运行bin下的webpack-dev-server,所以可以直接运行这个webpack-dev-server脚本,如在终端直接运行./node_modules/.bin/webpack-dev-server,也可以直接运行npm run serve。
webpack-dev-server 也称 WDS,会通过express搭建服务器,浏览器也自动更新
Hot Module Replacement,HMR,用于热更新
devServer: {
// contentBase已经弃用了,改用static
// contentBase:"./abc"
// 因为在上面用了CopyWebpackPublic,所以这里会自动默认打包了public的内容
static: {
// directory: './static',
directory: './public',
// directory: path.join(__dirname, 'static/abc.js'),
},
hot: true,
// host: "0.0.0.0",
port: 7777,
open: true,
// compress: true,
proxy: {
"/api": {
target: "http://localhost:8888",
pathRewrite: {
"^/api": ""
},
secure: false,
changeOrigin: true
}
}
},
resolve
用于模块解析,比如 require("./xxx"),其中xxx不用写后缀,就是通过这个,也可以设置别名,比如@
resolve: {
extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"],
alias: {
"@": path.resolve(__dirname, "./src"),
"js": path.resolve(__dirname, "./src/js")
}
},