图片等静态文件 dev prod通常来说,我们会通过使用file-loader,url-loader等loader来处理项目中的静态文件,如图片字体等文件
//这样最终dist文件中就会生成font文件夹存放字体文件[JavaScript] 纯文本查看 复制代码 {
test: /\.(woff|svg|eot|ttf)\??.*$/,
loader: "url-loader",
options: {
limit: 8192,
name: "font/[name].[hash:6].[ext]"
}
}
limit属性是在文件大小超出limit的值才会单独打包,否则使用base64 的方式引用通常适用于小图片,这就是我们通常的文件处理方式。
使用base64引入图片的好处是减少http请求数,但相应的问题是base64占用的空间比普通的图片文件大一点。
当然我们还有另外一种方案,具体做法是将项目的中静态文件统一存放在static文件夹下,最后使用 CopyWebpackPlugin将static文件夹拷贝到dist目录下
[JavaScript] 纯文本查看 复制代码 new CopyWebpackPlugin([
{
from: path.resolve(SRC_PATH, 'img'),
to: 'img'
}
]),
这样做的好处是我们的静态资源不经过webpack的处理,可以提升构建速度,但问题也是很明显的,那就是维护的成本增大并可能出现一些意外的情况,比如:
这样处理的问题是可能开发环境引用路径和打包文件访问图片路径不一致问题,这里可以通过output.publicPath属性来配置解决
[JavaScript] 纯文本查看 复制代码 output: {
//打包文件中通过相对路径引用的资源都会被配置的路径所替换
publicPath: '/assets/'
} [JavaScript] 纯文本查看 复制代码 //对于这种结构的项目当然不合适使用这种方法
|- /static
|– /components
| |– /my-component
| | |– index.jsx
| | |– index.css
| | |– icon.svg
| | |– img.png
当然从我们实际项目的测试效果来看,我只能说这种处理方式并不算是很优秀,仅供参考。
source map dev在开发环境中,我们比较关注调试的方便程度,而原始webpack打包后的bundle文件中可能包含来自多个文件的内容,对于程序的报错信息往往简单的指向这个bundle文件:
而source map是为了帮助我们定位程序出现的错误对应的源代码的位置。使用sourceMap报错信息正确的指向了源码的错误位置。
//1 使用devtool选项配置,有多个选项可选module.exports = { devtool: 'inline-source-map',};//2 使用plugins方式进行更细粒度的配置module.exports = { plugins: [ new webpack.SourceMapDevToolPlugin({ filename: '[name].js.map', exclude: ['vendor.js'] }) ]};//在使用uglifyjs-webpack-plugin时 需要开启sourceMap选项复制代码devtool文档
UglifyJsPlugin 配合 tree shaking prod对于js压缩 在webpack <= 3.x的版本中:
[JavaScript] 纯文本查看 复制代码 //1 使用 -p(production)标记来压缩js
//2 使用内置 plugin(webpack.optimize.UglifyJsPlugin)
//3 使用外部引入plugin(uglifyjs-webpack-plugin)
module.exports = {
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
//remove all comments
comments: false
}
}),
]
}; 在webpack4中[JavaScript] 纯文本查看 复制代码
[mw_shl_code=javascript,true]const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
//1 设置 mode
mode: ""production
//2 minimize
optimization:{
minimize: true,
//3 或者指定其他插件
minimizer: [
new UglifyJsPlugin({
sourceMap: true
})
]
},
//3 若需要sourceMap 需要设置 devtool的值
devtool: 'source-map',
};
//可选的压缩插件
UglifyJSPlugin, ClosureCompilerPlugin
BabelMinifyWebpackPlugin,
[/mw_shl_code]
此处需要注意。若是在使用了UglifyJSPlugin且开启sourceMap后,需要同时给devtool设置值。同样的若是设置了devtool的值,则UglifyJSPlugin也需要开启sourceMap。否则不会生成.map的源代码对应文件。
在开启js的压缩后 我们的tree shaking登场了,tree shaking是什么?为什么需要使用?
tree shaking是一个术语,用于描述移除js中未引用的代码。使用它能优化输出。未开启tree shaking的实例:
[JavaScript] 纯文本查看 复制代码 //tool.js
export function square(x) {
return x * x;
}
export function cube(x) {
return x * x * x;
}
//index.js
import { square } from "./tool.js"
//最终输出 在关闭 UglifyJSPlugin插件后测试结果
我们可以看到 cube这个我们并没有引用的模块也被打包进源码了。
使用tree shaking 来优化输出,在package.json中:
[JavaScript] 纯文本查看 复制代码 //webpack4
//1 将文件标记为无副作用
{
"name": "web",
"version": "1.0.0",
//若是整个项目都无副作用 直接设置为false
"sideEffects": false
//若是部分文件确实有副作用
"sideEffects": [file_path1, file_path2]
}
//2 开启js压缩 使用上述方法开启即可
//webpack2/3
//1 需要配置 .babelrc modules false
{
"presets": [
[
"env",
{
"modules": false
}
]
]
}
//2 开启js压缩
「副作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。
对于开启后的压缩代码中,我们搜索"*"号 只得到一个结果,测试成功。
最后我们简单解释下设置modules false的作用。tree shaking本身是依赖于ES6的静态导入,也就是我们常用的import export。ES6模块化中一个文件能够输出多个模块,而我们可以只导入需要的模块。对比commonjs的动态导入模块化标准,一个文件只有一个输出,因此不难发现,tree shaking在commonjs模块化的系统中是发挥不了作用的。
而modules的意义是启用将ES6模块语法转换为另一种模块类型,默认值'commonjs',将该设置为false即不转化,也就是ES6模块语法,所以在此我们需要将modules设置为false。
modules的取值有 'amd' | 'umd' | 'systemjs' | 'commonjs' | false。在webpack4中已经可以不用此方法来检测重复模块了
Split CSS prod一个web项目中css是关键的一环,若没有额外配置css最终会被打包进入js文件中,但熟悉浏览器渲染的开发者应该会清楚,浏览器在渲染页面时会解析DOM树和CSS树,最后将之对应合并呈现渲染好的页面。将css放在js中引入势必会延缓css树的计算。所以将css从js中分离,打包成单独的css文件,然后和js并行加载是我们项目的一个提升点,这样可以加快界面渲染速度,也可以单独做缓存。
[JavaScript] 纯文本查看 复制代码 //使用插件 extract-text-webpack-plugin
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
//用于css未被提取(allChunks: false)
fallback: "style-loader",
use: 'css-loader'
})
}
new ExtractTextPlugin({
filename: 'common.[chunkhash].css',
allchunk: true
})
当然webpack-dev-server是不支持extract-text-webpack-plugin抽离的css热替换的,所以此插件不建议再dev环境中使用,如果非要使用可以考虑css-hot-loader。
清理 /dist 文件夹 prodwebpack将打包的文件放在dist文件夹中,若是使用了hash文件名,则每次文件变动后重新打包生成的文件名都会不同,这会造成dist目录越来越混乱,好的做法是每次打包前先清理dist文件夹:
[JavaScript] 纯文本查看 复制代码 new CleanWebpackPlugin(pathsToClean, cleanOptions)
在内存中编译 devwebpack-dev-server大家都不陌生,开发环境必备,webpack内部依赖了webpack-hot-middleware,webpack-dev-middleware两个插件。
webpack-dev-middleware提供了在内存中编译功能,它在文件更改后自动编译文件并保存在内存中,具体表现为,刷新浏览器即可看到我们的更改。
webpack-hot-middleware提供了服务端推送功能,通常和webpack-dev-middleware配合使用,当文件更改并自动编译完成后,服务端通过SSE(服务端发送事件)将更改信息推送到客户端,客户端会接收到一个json文件,其中包含了更改了的文件的一些信息,客户端会根据这些信息主动向服务端获取最新的文件。
若无文件更改webpack-hot-middleware也会在一定时间间隔后遍历内存文件检测是否更改,然后通过事件流的方式向客户端发送消息。
webpack-dev-middleware和webpack-hot-middleware都是express的标准插件
我相信各位项目中这两个功能都是已经开启的我就不再具体说他们的配置了,这里我们主要说下在node服务端怎么使用这两个插件达到热更新的目的。
我们以koa为例,如何在koa中开启热更新调试我们的项目呢?
[JavaScript] 纯文本查看 复制代码 //新建 app.js作为koa服务端入口 app.js
import Koa from "koa";
import views from "koa-views";
import webpack from "webpack";
import webpack_config from "../webpack/webpack.config.js";
import { devMiddleware, hotMiddleware } from 'koa-webpack-middleware'
var app = new Koa()
var compiler = webpack(webpack_config)
app.use(views("./template", {map: {html: "ejs"}}));
app.use(devMiddleware(compiler,{
publicPath:"/"
}));
app.use(hotMiddleware(compiler))
koa-webpack-middleware 将express的中间件(webpack-dev-middleware和webpack-hot-middleware)进行封装,将我们koa中间件的next方法传递到express的第三个参数中进行封装。
最简单的配置如上。但这种配置会有一个问题就是刷新404的问题。
hotMiddleware会在匹配到项目跟路由时直接返回内存中的html文件给客户端。但是其他的路由如react的路由时,它不会去匹配,最终会返回一个404
[JavaScript] 纯文本查看 复制代码 //会返回template/index.html 但这时是空的
//也就是没有导入js的html
await ctx.render("index");
解决,当用户访问时在webpack编译输出的最后阶段获取到文件信息,取出获取到的html文件写入template下的index.html文件,最后返回它,具体操作如下:
[JavaScript] 纯文本查看 复制代码 compiler.plugin("emit",(comilation,callback) => {
const assets = comilation.assets;
let file, data;
Object.keys(assets).forEach(key => {
if(key.match(/\.html$/)){
file = path.resolve(__dirname,"./template/index.html");
data = assets[key].source();
fs.writeFileSync(file,data);
}
});
callback();
})
当然上述方法略显笨重,且需要理解的东西较多,不太推荐,这里有一个插件能解决上述问题 connect-history-api-fallback,大家自己学习下即可。
文章转载自:https://juejin.im/post/5bfb5c9c5188250c1021337c
|