前言
2020年1月18号,还有一周就是农历2020年的春节了,我提前请了6天的假,回家过年。此时公司的项目还是如火如茶的开展,一如既往的赶工。
项目太赶了,项目经理给了个模版这开发在里面开发,其间也没有什么时间增值学习。
公司配的联想t430笔记本屏幕没法看。自己拿了在这里上班的第一个月工资,以及卖掉了自己的游戏本,换了一个二手的macbook pro。
咬牙坚持了下来。
刚入行前端的头一年,我知道,我要的是知识,我要的不是没日没夜的加班,还有不停的百度,不停的试完一个博客的案例发现不合适又找下一个。
应了当前项目两个开发环境,三个打包路径改来改去的痛点,我决定在这个春节,怎么也要抽点时间出来看完这本《深入浅出Webpack》。
看的是电子书,2018年1月发售的,在京东上找,这本书还在买。看了京东对本书的简介好像并没有更新。书中用的webpack 是@2版并穿插@3版本。听说webpack 4有了相对大的更新,目前好像更新到了5,别说老油条,我一个新人,都觉得前端学起来费劲。
对于书上的安装方法来装各个工具是版本是不对的。但是能不能用,出现的坑,我都在下文的总结中列出来吧。
本次学习没有过深研究webpack,参照了vue-cli2.9版本搭建的webpack模版,以及webpack 4.4的官方中文文档(不知道怎么能看到旧文档),对于要在vue项目中用到的一些配置作了学习。
后期跟据需要会不断的完善这份总结。
有很多过来人都说前端更新快,看书学习不是最佳方法(考虑到书的价格不低),但是我还是比较喜欢看书,自己也买了很多书,这本是到目前为止看的唯一一本电子书。这本书到底值不值看?
前半部分对api的介绍没有webpack官方文档好理解,但是后半段对webpack的配置优化总结及一些按例还是很值得新手去看
开始吧!让我们学习进步起来!
DevServer
文中没有很明确说到如何启动build和运行webpack中的命令,
期实,运行webpack就是在终端中(进入当前文件夹)输入webpack,回车后项目就打包放在dist文件夹了,但是,这时打包的结果是一个js文件,入口index.html文件还是在dist同级目录下的那个index.html。
当你的webpack没有全局安装时,不能直接在终端中输入webpack,而是要输入webpack所在的文件,在目录node_modules/.bin/webpack中。直接输入
node_modules/.bin/webpack
回车。
运行devServer时也是在这个目录下
node_modules/.bin/webpack-dev-server
要看到热更新,还要改一下入口文件index.html中的bundle.js的路径,因为devserver不会理会webpack.config.js里的output.path属性。
<script src="./dist/bundle.js" ></script>
<!-- 改为下面的--!>
<script src="bundle.js"></script>
当然这些命令是可以写在package.json文件里的script下,然后通过npm run 去运行的
Entry
1. context(webpack默认的打包相对路径)
更改:在webpack的配置文件中:
const path = require('path');
{
context: path.resolve(__dirname,'app'),//或者 ./ ../ 等
}
2. 入口文件
entry: {
app: './src/main.js',//可以配置多个入口,或动态入口
}
3. output
output: {
filename: '[name].js',//[name]是node内置的name变量
path: path.resolve(__dirname, '../dist'),//必须是绝对路径,通过之path可以找出绝对路径。
publicPath: '',
}
说明:
filename 输出文件的名称
path 输出文件存放在本地的目录
publicPath 异步加载的地址,(发布到线上资源的URL前缀)
4. resolve
配置模块如何解析,
alias
改import后面的引用路径,从而达到简写
resolve: {
alias: {
"@": resolve('src'),//resolve方法是找到src是绝对地址
"vue$": './src/vue/'
}
}
//当引用
import Comp from 'vue$/vue';
import Button from '@/components/button';
//实际会被改成
import Comp from './src/vue/vue';
import Button from './src/components/button';
extensions
使import 的引用路径不用写相应的后缀名,
resolve: {
extensions: ['.js','.json'],//默认值
//extensions: ['.js','.vue','.json'],vue项目可以加入'.vue'
}
5. devServer
用于描述web pack-dev-server的行为选项
hot
模块热替换采用不刷新整个页面,而是热替换有变更的模块来更新浏览器视图,默认是刷新整个页面
devServer: {
hot: true,
}
historyApiFallback
用于方便开发使用了HTML5 History API的单页面应用
当设为:
devServer: {
historyApiFallback: true,
}
任意的 404
响应都可能需要被替代为 index.html
,但只能应用于只有一个html文件的应用。
如果要devServer能跟据不同的页面请求后回不周的html文件,配置:
historyApiFallback: {
rewrites: [
// /user开头的都返回user.html
{ from: /^\/user/, to: '/user.html'},
{ from: /^\/game/, to: './game.html'},
//其他的返回index.html
{ from: /./, to: '/index.html'}
]
}
compress
host
port
open
boolean,在devServer启动且第一次构建完成时,自动打开系统默认浏览器
proxy
使用了http-proxy-middleware
包,用于代理url。
直接代理:
devServer: {
proxy: {
"/api": "http://localhost:3000",
}
}
当请求/api/index
时会被代理到 'http://localhost:3000/api/index'
如果你不想始终传递 /api
,则需要重写路径:
proxy: {
"/api": {
target: "http://localhost:3000",
pathRewrite: {
"^/api": "",
}
}
}
如果是https
请求加入source: false,
proxy: {
source: false,//source直译:安全的
}
解决跨域:changeOrigin
proxy: {
changeOrigin: true,
}
本地就会虚拟一个服务器接收你的请求并代你发送该请求
quiet
boolean
启用 quiet
后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
webpack的其他零散配置
1. target
构建针对不同运行环境的代码
2. devtool
配置如何生成source Map,以方便调试
string
boolean
默认: false
不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
vue-cli2中应在development开发环境的devtool为:cheap-module-eval-source-map
对于适合开发环境的值,webpack文档中有如下说明:
eval
- 每个模块都使用eval()
执行,并且都有//@ sourceURL
。此选项会非常快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。
eval-source-map
- 每个模块使用eval()
执行,并且 source map 转换为 DataUrl 后添加到eval()
中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map。
cheap-eval-source-map
- 类似eval-source-map
,每个模块使用eval()
执行。这是 "cheap(低开销)" 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像eval
devtool。
cheap-module-eval-source-map
- 类似cheap-eval-source-map
,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。
Vue-cli2在build生产环境中的devtool为: #source-map
不知道为什么加了一个'#'
对于适合生产环境的值,webpack文档中有如下说明:
(none)
(省略devtool
选项) - 不生成 source map。这是一个不错的选择。
source-map
- 整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。你应该将你的服务器配置为,不允许普通用户访问 source map 文件!
hidden-source-map
- 与source-map
相同,但不会为 bundle 添加引用注释。如果你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会很有用。你不应将 source map 文件部署到 web 服务器。而是只将其用于错误报告工具。
nosources-source-map
- 创建的 source map 不包含sourcesContent(源代码内容)
。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。这仍然会暴露反编译后的文件名和结构,但它不会暴露原始代码。在使用
uglifyjs-webpack-plugin
时,你必须提供sourceMap:true
选项来启用 source map 支持。
其中,devtool有六个值,这六个值可以随意组合:
- eval: 用eval语句包裹需要安装的模块。
- source-map: 生成独立的source map文件
- hidden: 不在js文件中指出source map文件所在,这样浏览器就不会自动加载source map.
- inline: 将生成的source map转换成base64格式内嵌在js文件中。
- cheap: 在生成的source map中不会包含列信息,这样计算量更小,输出的source map文件更小;同时 loader输出的 source map不会被采用。
- module: 来自loader的 source map被简单处理成每一行一个模块。
3. watch和watchOptions
watch
webpack的监听模式,它支持监听文件更新,在文件发生变化时重新编译。
默认为false
在webpack-dev-server 和 web pack-dev-middleware中是默认开启的。
watchOption是相应的配置
npm script
也就是webpack的命令,写在package.json的script
字段里
代码检查
常用工具: ESlint、TSlint、stylelint
价绍了ESlint的一些设定,TSlint用于TypeScript,stylelint用于css
file-loader与url-loader
- File-loader
改变打包后的文件名,以方便寻找路径,用hash命名。
-
Url-loader
将小文件的资源改为base64格式以嵌入代码中。
好处:http/1协议中,每加载一次资源都要建立一次http请求,将小图片注入代码中可以减少这一次请求
不好处: 会导至js/css代码体积变大,文件过大将使网页加载缓慢。
module.export = { module: { rules: [ { test: /\.png$/, use: [ { loader: 'url-loader', options: { //30KB以下的文件采用url-loader, limit: 1024*03, //否则用file-loader, fallback: 'file-loader', } } ] } ] } }
webpack优化
踩坑1: 更改paralleUglifyPlugin并没有加快打包的速度
我的项目是用vue-cli2拉的默认环境,开发了一段时间,也加入了好多第三方包。平时的打包大概是50秒,改成paralleUglifyPlugin后,时间为48.5秒。
其中,配置时也有很多报错:正确的配置:
new ParallelUglifyJsPlugin({
//uglifyJs要改为uglifyES(求上传服务器验证)
uglifyES: {
output: {
beautify: false,
comments: false,
}
},
compress: {
warnings: false,
drop_console: true,
collapse_vars: true,
reduce_vars: true,
},
sourceMap: config.build.productionSourceMap,//false
parallel: true,
}),
区分环境
书中介绍的方法是设置process.env.NODE_ENV
const DefinePlugin = require('webpack/lib/DefinePlugin');
module.export = {
plugins: [
new DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
]
}
在定义环境时用JSON.stringify的原因是,环境烃量的值需要是一个由双引号包裹的字符串,而JSON.stringify('production')的值正好等于'"production"'
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。