用了哪些loader?
用了哪些plugin:
说下plugin和loader的区别
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
// [style-loader](/loaders/style-loader)
{ loader: 'style-loader' },
// [css-loader](/loaders/css-loader)
{
loader: 'css-loader',
options: {
modules: true
}
},
// [sass-loader](/loaders/sass-loader)
{ loader: 'sass-loader' }
]
}
]
}
}
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
plugins: [new CompressionPlugin()],
};
And run webpack via your preferred method.
为什么要用webpack
webpack的工作流程是什么?
模块打包原理?
webpack为每个文件提供一个导入导出环境,不影响原来的代码逻辑跟顺序
文件监听原理?
当代码发生改变的时候,自动构建新的输出文件;
轮训判断是否改变,如果某个文件发生改变,不会立刻告诉监听器,先会缓存起来,等到aggregateTimeout时间后再改变。
确定是:需要刷新浏览器手动刷新
两种方式:
module.export = {
// 默认false,也就是不开启
watch: true,
// 只有开启监听模式时,watchOptions才有意义
watchOptions: {
// 默认为空,不监听的文件或者文件夹,支持正则匹配
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行,默认300ms
aggregateTimeout:300,
// 判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
poll:1000
}}
说下webpack的热更新原理?
Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
vue文件 保存时 浏览器不刷新,页面改动的话,拿到带有hash的文件:
具体流程如下:
以下截图为send页面,改动页面时请求的数据,当本地资源发生变化时,实际上 webpack-dev-server 与浏览器之间维护了一个 Websocket,webpack-dev-server 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比,改变的话就把当前请求生成一个hash,客户端比对发现差异之后,会向webpack-dev-server发起ajax再次请求带有hash后缀的文件,下次再改变的时候生成的send文件加上该hash后缀。
当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。这又是我们项目里最常见的之前的业务弹层都没了 直接刷新当前页面。
文件指纹就是文件的后缀
vue-cli是asset文件下都用hash;图片,svg,media,fonts;路径packages/@vue/cli-service/lib/config/assets.js
2. contenthash:根据文件内容来定义hash,contenthash就更新;常用于css, js
Vue-cli源码打包
module.exports = {
entry: {
app: './scr/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path:__dirname + '/dist'
},
plugins:[
new MiniCssExtractPlugin({
filename: `[name][contenthash:8].css`
})
]}
if (useThreads) {
addLoader({
name: 'thread-loader',
loader: require.resolve('thread-loader'),
options:
typeof projectOptions.parallel === 'number'
? { workers: projectOptions.parallel }
: {}
})
}
webpackConfig.module..alias.set('@', api.resolve('src'))
packages/@vue/cli-service/lib/config/app.js
// vue-cli的源码 code splitting
if (process.env.NODE_ENV !== 'test') {
webpackConfig.optimization.splitChunks({
cacheGroups: {
defaultVendors: {
name: `chunk-vendors`, // 使用三方的
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: `chunk-common`, // 公共的
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
})
}
addLoader({
name: 'cache-loader',
loader: require.resolve('cache-loader'),
options: api.genCacheConfig('ts-loader', {
'ts-loader': require('ts-loader/package.json').version,
'typescript': require('typescript/package.json').version,
modern: !!process.env.VUE_CLI_MODERN_BUILD
}, 'tsconfig.json')
})
if (process.env.NODE_ENV === 'production') {
const TerserPluginV4 = require('terser-webpack-plugin')
config.optimization.minimizer('terser').init(
(Plugin, [terserPluginOptions]) =>
new TerserPluginV4({
sourceMap: rootOptions.productionSourceMap,
cache: true,
...terserPluginOptions
})
)
// inject CSS extraction plugin 注入提取css插件
if (shouldExtract) {
webpackConfig
.plugin('extract-css')
.use(require('mini-css-extract-plugin'), [extractOptions])
// minify extracted CSS 压缩提取的css
webpackConfig.optimization
.minimizer('css')
.use(require('css-minimizer-webpack-plugin'), [{
parallel: rootOptions.parallel, // 多进程并行压缩
sourceMap: rootOptions.productionSourceMap && sourceMap,
minimizerOptions: cssnanoOptions
}])
}
const inlineLimit = 4096
const vueMajor = getVueMajor(api.getCwd())
const supportsEsModuleAsset = (vueMajor !== 2)
const genAssetSubPath = dir => {
return getAssetPath(
options,
`${dir}/[name]${options.filenameHashing ? '.[hash:8]' : ''}.[ext]`
)
}
const genUrlLoaderOptions = dir => {
return {
limit: inlineLimit,
esModule: supportsEsModuleAsset,
// use explicit fallback to avoid regression in url-loader>=1.1.0
fallback: {
loader: require.resolve('file-loader'),
options: {
name: genAssetSubPath(dir),
esModule: supportsEsModuleAsset
}
}
}
}
api.chainWebpack(webpackConfig => {
webpackConfig.module
.rule('images')
.test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
.use('url-loader')
.loader(require.resolve('url-loader'))
.options(genUrlLoaderOptions('img'))
// do not base64-inline SVGs.
// https://github.com/facebookincubator/create-react-app/pull/1180
webpackConfig.module
.rule('svg')
.test(/\.(svg)(\?.*)?$/)
.use('file-loader')
.loader(require.resolve('file-loader'))
.options({
name: genAssetSubPath('img'),
esModule: supportsEsModuleAsset
})
webpackConfig.module
.rule('media')
.test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
.use('url-loader')
.loader(require.resolve('url-loader'))
.options(genUrlLoaderOptions('media'))
webpackConfig.module
.rule('fonts')
.test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
.use('url-loader')
.loader(require.resolve('url-loader'))
.options(genUrlLoaderOptions('fonts'))
})
代码分隔的本质是什么 (splitChunksPlugin,commonChunkPlugin)
在源代码和打包一个文件之间找个平衡,在服务器能承受的压力下达到更好的用户体验。
其实就是按需加载 ,三方的依赖js打包成vendor.hash[8].js 搞成缓存 使用HashedModulePlus记住id不改变,公共的组件打包成common.hash[8].js
其他的尽量按需加载,按照模块放在一个chunkName里面
有自己写过plugin吗?
没有 但是可以参考vue-cli里的packages/@vue/cli-service/lib/webpack/ModernModePlugin.js
具体可看官网Plugin API
自己写过loader吗?
具体可看官网loader API