使⽤script的module属性实现es6以上的兼容作⽤:
借⽤ vue-cli3 中⽂档的⼏句话来说明下他的作⽤:
现代版的包会通过<script type="module">在被⽀持的浏览器中加载 (他的语法是 es6 以上的,可以直接运⾏)
旧版的包会通过<script nomodule>加载,并会被⽀持 ES modules 的浏览器忽略。
修改过程:
⾸先下载需要的包:
下⾯列出:
"babel-core": "^6.26.0"
"babel-plugin-syntax-dynamic-import": "^6.18.0"
"babel-plugin-transform-class-properties": "^6.24.1"
"babel-polyfill": "^6.26.0"
"babel-preset-env": "^1.7.0"
"babel-preset-react": "^6.24.1"
"html-webpack-add-module-plugin": "^1.0.3"
"uglifyjs-webpack-plugin": "^1.2.7"
去除 package.json 中的 babel 参数
复制 /fig.prod.js ⼀份在当前⽬录, 命名为 fig.prod.es5.js
在 prod.js 中:
添加引⽤:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const htmlWebpackAddModulePlugin = require('html-webpack-add-module-plugin')
const fs = require('fs')
说明:
UglifyJsPlugin 是因为 webpack.optimize.UglifyJsPlugin ⽆法压缩 es6 以上的代码所以需要该插件
htmlWebpackAddModulePlugin 是可以将⽣成的 script 转换为 module 或者 nomodule 的插件
fs 是可以对于⽂件进⾏⼀系列操作,这⾥只是⽤来判断⽂件是否存在
修改代码:
修改 oneOf 中的test: /\.(js|jsx|mjs)$/该 loader 将其 options 改为
options: {
presets: [
['env', {
modules: false,
useBuiltIns: true,
targets: {
browsers: [
'Chrome >= 60',
'Safari >= 10.1',
'iOS >= 10.3',
'Firefox >= 54',
'Edge >= 15',
]
},
}],
"react",
]
,
plugins: ["transform-class-properties", "syntax-dynamic-import"],
compact: true
}
可以将include: paths.appSrc去除(注意,如果这样做,可能会引起某些错误)
在 plugins 中添加插件:
new htmlWebpackAddModulePlugin({
module: 'all',
}),
new UglifyJsPlugin(),
注释 webpack.optimize.UglifyJsPlugin 插件:
/
/ new webpack.optimize.UglifyJsPlugin({
//  compress: {
//    warnings: false,
//    // Disabled because of an issue with Uglify breaking seemingly valid code:
//    // github/facebookincubator/create-react-app/issues/2376
//    // Pending further investigation:
//    // github/mishoo/UglifyJS2/issues/2011
//    comparisons: false,
//  },
//  mangle: {
//    safari10: true,
/
/  },
//  output: {
//    comments: false,
//    // Turned on because emoji and regex is not minified properly using default
//    // github/facebookincubator/create-react-app/issues/2488
//    ascii_only: true,
//  },
//  sourceMap: shouldUseSourceMap,
// }),
修改 HtmlWebpackPlugin 插件为:
new HtmlWebpackPlugin({
inject: true,
template: fs.existsSync(`${paths.appBuild}/index.html`) ? `${paths.appBuild}/index.html` : paths.appHtml,      minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
在 fig.prod.es5.js 中修改
添加包引⽤:
const htmlWebpackAddModulePlugin = require('html-webpack-add-module-plugin')
修改⼊⼝名:
entry: {
'main.es5': [solve('./polyfills'),"babel-polyfill", paths.appIndexJs]
},
与之前⼀样的修改 oneOf 中的 babel loader 的 options:
options: {
presets: [
['env', {
modules: false,
useBuiltIns: true,
targets: {
browsers: [
"> 1%",
'last 2 version',
'firefox ESR'
]
},
}],
"react"
],
plugins: ["transform-class-properties", "syntax-dynamic-import"],
compact: true,
},
添加插件:
new htmlWebpackAddModulePlugin({
nomodule: 'all',
removeCSS: 'main'
}),
开始修改 /scripts/build.js ⽂件:
添加 es5 config ⽂件的引⽤:
javascript说明const es5config = require('../fig.prod.es5');
在 build 函数之前添加函数:
function compiler(config, previousFileSizes, prevResult) {
return new Promise((resolve, reject) => {
config.run((err, stats) => {
if (err) {
return reject(err);
}
const messages = Json({}, true));      if (s.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (s.length > 1) {
}
return reject(new s.join('\n\n')));
}
if (
(v.CI !== 'string' ||
messages.warnings.length
) {
console.log(
'\nTreating warnings as errors v.CI = true.\n' +            'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
// console.log(stats)
let result = {
stats,
previousFileSizes,
warnings: messages.warnings,
}
if (prevResult) {
result.prevResult = prevResult
}
return resolve(result);
});
});
}
修改刚刚的 build 函数为:
async function build(previousFileSizes) {
console.log('Creating an optimized ');
let modernConfig = webpack(config);
let es5Config = webpack(es5config)
let result = await compiler(es5Config, previousFileSizes);
// remove main.es5.css
let arr = Object.keys(result.statspilation.assets)
const path = arr.find(v => v.indexOf('css') > -1 && v.indexOf('main') > -1)  ve( + '/' + path)
result = await compiler(modernConfig, previousFileSizes, result);
return result
}
在 /public/index.html 中的
后⾯添加:
<script>
(function() {
var check = ateElement('script');
if (!('noModule' in check) && 'onbeforeload' in check) {
var support = false;
document.addEventListener('beforeload', function(e) {
if (e.target === check) {
support = true;
} else if (!e.target.hasAttribute('nomodule') || !support) {
return;
}
e.preventDefault();
}, true);
check.src = '.';
document.head.appendChild(check);
}
}());
</script>
解决 safari 的重复加载问题
基础的修改到此为⽌了,接下来运⾏指令 : npm run build即可
Build 注意点
虽然现在有⼀个规范,模块的JS必须添加mjs后缀,但是如果这样做,你不能在本地构建后运⾏HTML⽂件,你必须在服务器上运⾏它,否则你报错:
Failed to load module script: The server responded with a non-JavaScript MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec. Build 结果
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="./manifest.json">
<link rel="shortcut icon" href="./favicon.ico">
<title>React App</title>
<script>!function () {
var t = ateElement("script");
if (!("noModule" in t) && "onbeforeload" in t) {
var n = !1;
document.addEventListener("beforeload", function (e) {
if (e.target === t) n = !0; else if (!e.target.hasAttribute("nomodule") || !n) return;
e.preventDefault()
}, !0), t.type = "module", t.src = ".", document.head.appendChild(t), t.remove()
}
}()</script>
<link href="./static/css/main.c17080f1.css" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./static/js/main.es5.bfc0d013.js" nomodule></script>
<script src="./static/0168c.js" type="module"></script>
</body>
</html>
⼤⼩⽐较:
两个 main 版本只相差 async/await 和 polyfill 的转译:
main.js :123k
main.es5.js :220k
两个 chunk 相差⼀个 async/await 的转译:
es6:
0.chunk.js : 362b = 0.29k
es5:
0.chunk.js : 2k
这⾥借⽤开头⽂章的运⾏速度表格(他是没有加上 babel-polyfill 的):
Version Parse/eval time (individual runs)Parse/eval time (avg)
ES2015+ (main.mjs)184ms, 164ms, 166ms172ms
ES5 (main.es5.js)389ms, 351ms, 360ms367ms
结论
算是⼀种⽣硬的实现⽅案, webpack 4的异步组件还未测试
缺点是 webpack 重复⽣成,会减慢 build 的时间
vue-cli3 已经有了这种⽅式,期待下 react-script 的官⽅指令
解决 css 的问题,但是 es5 的代码⼤⼩不会打印出来

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。