处理JS语法及校验
上一节实现了如何将js的高级语法转低级语法。继续来学习
前言
1. 公用的代码没有提取出来
更改a.js文件
// webpack-dev-1\src\a.js
module.exports = 'xzz'
// 在b.js增加一个类 看一下打包效果
class B {
}
使用npm run build
,打包看一下效果
会有一个非常恶心的事。
我们之前在index.js中定义的A的类有一个classCallCheck,
然后刚刚定义的类B也有一个classCallCheck,
每次校验时,都会生成一份这样的校验代码,非常冗余。
所以,公用的代码并没有提取出来。
2 高级语法转换后的语法没有定义出来
再来看一个问题。
我们继续修改a.js
// webpack-dev-1\src\a.js
module.exports = 'xzz'
// 在b.js增加一个类 看一下打包效果
class B {
}
// 增加一个generator语法
function* gen(parmas) {
yield 1
}
console.log(gen().next());
然后我们运行在浏览器看一下npm run dev
,会发现一个问题
虽然我们已经将ES6转化为了ES5,但是这个generator语法是内置的api,转化的时候并不会转化api,转化之后也不会内置转换的方法,
为什么会报错呢,原因是,打包完之后将generator进行转化了,但是转换后的方法,并没有被定义出来,
同样的,如果用更高级的promise也不会定义,这时候就需要安装一个包来帮我们干这件事。
是一个babel代码运行时的一个包。
@babel/plugin-transform-runtime,这个包只要配置基本都会被用到
安装一下
# @babel/plugin-transform-runtime -D
yarn add @babel/plugin-transform-runtime@^7.4.4 -D
同时需要一个@babel/runtime,这个包上线的时候也需要,会为一些高级语法打一些补丁,所以安装的时候,不带-D了
# @babel/runtime
yarn add @babel/runtime
修改一下配置文件webpack.config.js
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件
let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件
module.exports = {
// 配置优化项
optimization: {
minimizer: [ // 是一个数组,还得优化js
new UglifyJsPlugin({
cache: true, // 是否用缓存
parallel: true, // 是否并发压缩
sourceMap: true,// 线上用来调试的映射文件
}),
new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
]
},
devServer: { // 开发服务器的配置
port: 3000, // 默认端口是8080,这里可以改
progress: true, // 打包时候的进度条
contentBase: './build', // 以哪个文件夹作为服务的根目录
open: true, // 服务启动完毕后,直接打开浏览器
compress: true, // 启动gzip压缩
},
mode: 'development', // 模式,默认两种模式 production 和 development
entry: './src/index.js', // 入口
output: {
// 带hash戳的文件,:8限制一下hash戳的长度
filename: 'bundle.[hash:8].js', // 打包后的文件名
path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
},
plugins: [ // 是一个数组,放着所有的webpack插件
// 插件是一个类,通过new的方式来引用
new HtmlWebpackPlugin({
template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
hash: true, // 引用的时候可以加一个hash戳
}),
// 插件的使用就没有先后顺序了,随便放就行
// 引入抽离css样式的插件,
new MiniCssExtractPlugin({
filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
}),
],
module: { // 模块
rules: [ // 规则,在这里面配置各种loader
{
test: /\.js$/, // 匹配以js结尾的文件
use: {
loader: 'babel-loader',
options: { // 用babel-loader 需要把ES6转为ES5
// 配置可以写在这里,还可以写在外面
// 在这里添加一个预设
presets: [ // 这是一个大插件的集合
'@babel/preset-env', // 这个插件就可以把ES6转ES5
],
// 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
['@babel/plugin-proposal-class-properties', { "loose": true }],
"@babel/plugin-transform-runtime"
]
}
},
include: path.resolve(__dirname, 'src'), // 只找src目录下的js 包括
exclude: /node_modules/ // node_modules文件夹下的文件不用找 排除
},
// css-loader 解析css文件,@import语法的
// style-loader 把解析后的css文件 插入到head标签中
// loader有个特点,希望单一,一个loader干一件事
/*
loader的用法
1. 只用字符串,就是只用一个loader
2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
*/
// loader的顺序,默认是从右向左执行,从下往上执行
{
test: /\.css$/,
use: [
// 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
MiniCssExtractPlugin.loader,
'css-loader',
// 应该在解析css之前增加前缀
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
// 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
// 这里就用一个了
'css-loader', // 解析 @import语法 解析 css
// 应该在解析css之前增加前缀
'postcss-loader',
'less-loader' // 把less 转换为 css
]
},
],
}
}
再次打包,就会发现出现了generator的语法包
并且会把所有的公用代码抽离出去,比如刚才的classCallCheck,所有公共引用都通过了babel_runtime_helpers来引入的。
这里的不知道版本哪里出问题了,在浏览器运行时有报错,先放一放,继续学下面的内容
3 还有一些更高级的语法需要使用pollyfill
3 如果使用更高级的语法,还是会报错 比如ES7的语法 includes
修改a.js文件
// webpack-dev-1\src\a.js
module.exports = 'xzz'
// 在b.js增加一个类 看一下打包效果
class B {
}
// 增加一个generator语法
function* gen(parmas) {
yield 1
}
console.log(gen().next());
// 增加一个es7的includes语法,如果不配置仍旧不会转换
'abc'.includes('a')
打包看下效果
发现并没有并转换,需要安装 @babel/polyfill 不加-D,因为需要引入到代码里
yarn add @babel/polyfill
不知道版本,安个最新的吧
把polyfill引入代码里,修改a.js文件
// webpack-dev-1\src\a.js
module.exports = 'xzz'
// 在b.js增加一个类 看一下打包效果
class B {
}
// 增加一个generator语法
function* gen(parmas) {
yield 1
}
console.log(gen().next());
require('@babel/polyfill') // 引入polyfill
// 会帮我们实现一些不兼容的语法,比如下面的includes会重写
// 增加一个es7的includes语法,如果不配置仍旧不会转换
'abc'.includes('a')
再重新打包一下看下效果,帮我们自己实现了一下。
写代码时增加校验 Eslint
我们在写代码时都需要一个校验器,校验一下代码是否符合规范。
可以使用eslint,官网里又demo,在demo处可以勾选配置规则,然后下载,导入本地,我们随便勾选几个,直接默认了,然后把下载好的文件放在根目录,文件名.
// webpack-dev-1\.eslintrc.json
{
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "script",
"ecmaFeatures": {}
},
"rules": {
"constructor-super": 2,
"for-direction": 2,
"getter-return": 2,
"no-async-promise-executor": 2,
"no-case-declarations": 2,
"no-class-assign": 2,
"no-compare-neg-zero": 2,
"no-cond-assign": 2,
"no-const-assign": 2,
"no-constant-condition": 2,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-dupe-args": 2,
"no-dupe-class-members": 2,
"no-dupe-else-if": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 2,
"no-empty-character-class": 2,
"no-empty-pattern": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast": 2,
"no-extra-semi": 2,
"no-fallthrough": 2,
"no-func-assign": 2,
"no-global-assign": 2,
"no-import-assign": 2,
"no-inner-declarations": 2,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-loss-of-precision": 2,
"no-misleading-character-class": 2,
"no-mixed-spaces-and-tabs": 2,
"no-new-symbol": 2,
"no-nonoctal-decimal-escape": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-prototype-builtins": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-self-assign": 2,
"no-setter-return": 2,
"no-shadow-restricted-names": 2,
"no-sparse-arrays": 2,
"no-this-before-super": 2,
"no-undef": 2,
"no-unexpected-multiline": 2,
"no-unreachable": 2,
"no-unsafe-finally": 2,
"no-unsafe-negation": 2,
"no-unsafe-optional-chaining": 2,
"no-unused-labels": 2,
"no-unused-vars": 2,
"no-useless-backreference": 2,
"no-useless-catch": 2,
"no-useless-escape": 2,
"no-with": 2,
"require-yield": 2,
"use-isnan": 2,
"valid-typeof": 2
},
"env": {}
}
修改配置文件
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件
let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件
module.exports = {
// 配置优化项
optimization: {
minimizer: [ // 是一个数组,还得优化js
new UglifyJsPlugin({
cache: true, // 是否用缓存
parallel: true, // 是否并发压缩
sourceMap: true,// 线上用来调试的映射文件
}),
new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
]
},
devServer: { // 开发服务器的配置
port: 3000, // 默认端口是8080,这里可以改
progress: true, // 打包时候的进度条
contentBase: './build', // 以哪个文件夹作为服务的根目录
open: true, // 服务启动完毕后,直接打开浏览器
compress: true, // 启动gzip压缩
},
mode: 'development', // 模式,默认两种模式 production 和 development
entry: './src/index.js', // 入口
output: {
filename: 'bundle.js', // 打包后的文件名
path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
},
plugins: [ // 是一个数组,放着所有的webpack插件
// 插件是一个类,通过new的方式来引用
new HtmlWebpackPlugin({
template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
hash: true, // 引用的时候可以加一个hash戳
}),
// 插件的使用就没有先后顺序了,随便放就行
// 引入抽离css样式的插件,
new MiniCssExtractPlugin({
filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
}),
],
module: { // 模块
rules: [ // 规则,在这里面配置各种loader
{
test: /\.js$/,
use: {
loader: 'eslint-loader',
options: {
enforce: 'pre', // loader默认从右向左 从下向上执行,增加这个属性,就代表最先执行它
// 默认是normal 还有一个值为 post 代表最后执行
}
}
},
{
test: /\.js$/, // 匹配以js结尾的文件
use: {
loader: 'babel-loader',
options: { // 用babel-loader 需要把ES6转为ES5
// 配置可以写在这里,还可以写在外面
// 在这里添加一个预设
presets: [ // 这是一个大插件的集合
'@babel/preset-env', // 这个插件就可以把ES6转ES5
],
// 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
['@babel/plugin-proposal-class-properties', { "loose": true }],
"@babel/plugin-transform-runtime"
]
}
},
include: path.resolve(__dirname, 'src'), // 只找src目录下的js 包括
exclude: /node_modules/ // node_modules文件夹下的文件不用找 排除
},
// css-loader 解析css文件,@import语法的
// style-loader 把解析后的css文件 插入到head标签中
// loader有个特点,希望单一,一个loader干一件事
/*
loader的用法
1. 只用字符串,就是只用一个loader
2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
*/
// loader的顺序,默认是从右向左执行,从下往上执行
{
test: /\.css$/,
use: [
// 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
MiniCssExtractPlugin.loader,
'css-loader',
// 应该在解析css之前增加前缀
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
// 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
// 这里就用一个了
'css-loader', // 解析 @import语法 解析 css
// 应该在解析css之前增加前缀
'postcss-loader',
'less-loader' // 把less 转换为 css
]
},
],
}
}
安装
yarn add eslint@^5.16.0 eslint-loader@^2.1.2 -D
再次打包,就会出现语法校验
总结
在配置babel时,一些高级语法或者不支持的语法,还需要配置额外的插件
比如 generator promise等
或者有一些需要polyfill的语法,直接在代码里引用第三方包
在进行代码校验时,配置eslint插件即可