let value = 'a'
// babel编译后:
var value = 'a'
let value = 'a'
// babel编译后:
var value = 'a'
可以看到 Babel是将let编译成了var,那再来一个例子:
if (false) {
let value = 'a';
}
console.log(value); // value is not defined
if (false) {
let value = 'a';
}
console.log(value); // value is not defined
如果babel将let编译为var应该打印 undefined,为何会报错呢,babel是这样编译的:
if (false) {
var _value = 'a';
}
console.log(value);
if (false) {
var _value = 'a';
}
console.log(value);
babel是改变量名,使内外层的变量名称不一样。
const修改值时报错,以及重复声明报错怎么实现的呢?其实在编译时就报错了。
重点来了:for循环中的 let 声明呢?
var functions = [];
for (let i = 0; i < 3; i++) {
functions[i] = function () {
console.log(i);
};
}
functions[0](); // 0
var functions = [];
for (let i = 0; i < 3; i++) {
functions[i] = function () {
console.log(i);
};
}
functions[0](); // 0
babel编译成了:
var functions = [];
var loop = function loop(i) {
functions[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 3; i++) {
loop(i);
}
functions[0](); // 0
var functions = [];
var loop = function loop(i) {
functions[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 3; i++) {
loop(i);
}
functions[0](); // 0
const webpackConfig = {
entry: {
bundle: resolve('src/index.lint.jsx'),
componentsInFolders: glob.sync(resolve('src/components/*/*.js?(x)')),
componentsInRoot: glob.sync(resolve('src/components/*.js?(x)')),
api: glob.sync(resolve('src/api/*.js?(x)')),
utils: glob.sync(resolve('src/utils/*.js?(x)')),
},
output: {
path: resolve('dist'),
filename: '[name].js',
publicPath: '',
},
externals: {
'zepto': 'window.$',
'highcharts': 'window.Highcharts',
'jsencrypt': 'window.JSEncrypt',
},
resolve: {
extensions: ['.lint.jsx', '.jsx', '.js', '.css', '.ejs', '.scss', '.json', '.sass'],
alias: {
'@': resolve('src'),
'@img': resolve('src/assets/img'),
'@utils': resolve('src/utils'),
'@css': resolve('src/assets/css'),
'@component': resolve('src/components'),
},
},
resolveLoader: {
modules: [
'node_modules',
],
},
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
'happypack/loader?id=babel',
conditionalCompiler,
],
include: [resolve('src')],
},
{
test: /\.(png|jpg|gif|svg|mp3)$/,
loader: 'file-loader',
exclude: /node_modules/,
query: {
name: 'assets/img/[name].[ext]',
// name: 'assets/img/[path][module-img][name].[ext]',
},
},
{
test: /\.less$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'postcss-loader' },
{
loader: 'less-loader',
options: {
modifyVars: require('../package').theme,
javascriptEnabled: true,
},
},
],
},
],
},
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: dllManifestForVendorCxg,
}),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: dllManifestForVendorCxgOthers,
}),
new webpack.LoaderOptionsPlugin({
options: {
customInterpolateName (url) {
if (/\[module-img\]/.test(url)) {
url = url.replace(/img[\\/]/g, '')
url = url.replace(/\[module-img\]/, '')
const folderName = url.replace(/^.*[\\/]([\w-]+)[\\/][\w-]+\.[\w]+/, '$1')
const fileName = url.replace(/^.+[\\/]([\w-]+\.[\w]+$)/, '$1')
url = `assets/img/${folderName}/${fileName}`
return url
}
return url
},
},
}),
new HappyPack({
id: 'babel',
threads: os.cpus().length,
verbose: false,
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: 'node_modules/.cache/babel-loader',
},
},
],
}),
new CopyWebpackPlugin([
{
from: resolve('src/hbenv.js'),
to: './assets/js/hbenv.js',
},
{
from: resolve('example'),
to: './example',
},
{
from: resolve('favicon.ico'),
to: './',
},
]),
],
}
module.exports = webpackConfig
const webpackConfig = {
entry: {
bundle: resolve('src/index.lint.jsx'),
componentsInFolders: glob.sync(resolve('src/components/*/*.js?(x)')),
componentsInRoot: glob.sync(resolve('src/components/*.js?(x)')),
api: glob.sync(resolve('src/api/*.js?(x)')),
utils: glob.sync(resolve('src/utils/*.js?(x)')),
},
output: {
path: resolve('dist'),
filename: '[name].js',
publicPath: '',
},
externals: {
'zepto': 'window.$',
'highcharts': 'window.Highcharts',
'jsencrypt': 'window.JSEncrypt',
},
resolve: {
extensions: ['.lint.jsx', '.jsx', '.js', '.css', '.ejs', '.scss', '.json', '.sass'],
alias: {
'@': resolve('src'),
'@img': resolve('src/assets/img'),
'@utils': resolve('src/utils'),
'@css': resolve('src/assets/css'),
'@component': resolve('src/components'),
},
},
resolveLoader: {
modules: [
'node_modules',
],
},
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
'happypack/loader?id=babel',
conditionalCompiler,
],
include: [resolve('src')],
},
{
test: /\.(png|jpg|gif|svg|mp3)$/,
loader: 'file-loader',
exclude: /node_modules/,
query: {
name: 'assets/img/[name].[ext]',
// name: 'assets/img/[path][module-img][name].[ext]',
},
},
{
test: /\.less$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'postcss-loader' },
{
loader: 'less-loader',
options: {
modifyVars: require('../package').theme,
javascriptEnabled: true,
},
},
],
},
],
},
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: dllManifestForVendorCxg,
}),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: dllManifestForVendorCxgOthers,
}),
new webpack.LoaderOptionsPlugin({
options: {
customInterpolateName (url) {
if (/\[module-img\]/.test(url)) {
url = url.replace(/img[\\/]/g, '')
url = url.replace(/\[module-img\]/, '')
const folderName = url.replace(/^.*[\\/]([\w-]+)[\\/][\w-]+\.[\w]+/, '$1')
const fileName = url.replace(/^.+[\\/]([\w-]+\.[\w]+$)/, '$1')
url = `assets/img/${folderName}/${fileName}`
return url
}
return url
},
},
}),
new HappyPack({
id: 'babel',
threads: os.cpus().length,
verbose: false,
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: 'node_modules/.cache/babel-loader',
},
},
],
}),
new CopyWebpackPlugin([
{
from: resolve('src/hbenv.js'),
to: './assets/js/hbenv.js',
},
{
from: resolve('example'),
to: './example',
},
{
from: resolve('favicon.ico'),
to: './',
},
]),
],
}
module.exports = webpackConfig
通过配置不同的 entry 来生成不同的 chunk:
hash
hash是跟整个项目的构建相关,只要项目里有文件更改, 整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值
chunkhash
采用hash计算的话,每一次构建后生成的哈希值都不一样, 即使文件内容压根没有改变。这样子是没办法实现缓存效果, 我们需要换另一种哈希值计算方式,即chunkhash。
chunkhash和hash不一样, 它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk, 生成对应的哈希值。 我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建, 接着我们采用chunkhash的方式生成哈希值, 那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响。
contenthash
在chunkhash的例子,我们可以看到由于index.css被index.js引用了, 所以共用相同的chunkhash值。 但是这样子有个问题,如果index.js更改了代码, css文件就算内容没有任何改变,由于是该模块发生了改变, 导致css文件会重复构建。
这个时候,我们可以使用extra-text-webpack-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建。
var extractTextPlugin = require('extract-text-webpack-plugin'),
path = require('path')
module.exports = {
...
...
output: {
path: path.join(__dirname, '/dist/js'),
filename: 'bundle.[name].[chunkhash].js',
},
plugins: [
new extractTextPlugin('../css/bundle.[name].[contenthash].css')
]
}
var extractTextPlugin = require('extract-text-webpack-plugin'),
path = require('path')
module.exports = {
...
...
output: {
path: path.join(__dirname, '/dist/js'),
filename: 'bundle.[name].[chunkhash].js',
},
plugins: [
new extractTextPlugin('../css/bundle.[name].[contenthash].css')
]
}
const loaderUtils = require('loader-utils');
module.exports = function (source /* 逐个处理的文件内容 */) {
const self = this
const options = loaderUtils.getOptions(self)
const resourcePath = self.resourcePath
// 根据上面的一些信息处理resource
return resource
}
const loaderUtils = require('loader-utils');
module.exports = function (source /* 逐个处理的文件内容 */) {
const self = this
const options = loaderUtils.getOptions(self)
const resourcePath = self.resourcePath
// 根据上面的一些信息处理resource
return resource
}
module.exports = class FixedChunkIdPlugin {
constructor (options) {
this.options = options || {}
}
apply (compiler) {
compiler.plugin('compilation', (compilation) =>
compilation.plugin('before-chunk-ids', (chunks) => {
chunks.forEach((chunk) => {
if (!chunk.id) {
// 要求定义路由的chunk名时不要重名
// 我们现有的逻辑,如果页面chunk名重复的话会生成同一个页面js,本来就是不允许重名的
chunk.id = chunk.name
}
})
})
)
}
}
module.exports = class FixedChunkIdPlugin {
constructor (options) {
this.options = options || {}
}
apply (compiler) {
compiler.plugin('compilation', (compilation) =>
compilation.plugin('before-chunk-ids', (chunks) => {
chunks.forEach((chunk) => {
if (!chunk.id) {
// 要求定义路由的chunk名时不要重名
// 我们现有的逻辑,如果页面chunk名重复的话会生成同一个页面js,本来就是不允许重名的
chunk.id = chunk.name
}
})
})
)
}
}