安裝 node.js 當前 node.js 版本 :v12.13.1 當前 npm 版本 : 6.12.1javascript
本文從零開始搭建 webpack ,只須要按照步驟一步一步走,最後就可搭建成功 ,請放心食用,無毒css
mkdir webpack4-react && cd webpack4-react
npm init -y
複製代碼
當前 webpack 版本:4.41.5 當前 webpack-cli 版本:3.3.10html
npm install --save-dev webpack webpack-cli
複製代碼
或java
yarn add webpack webpack-cli --dev
複製代碼
調整 package.json 文件,以便確保咱們安裝包是私有的(private),而且移除 main 入口。這能夠防止意外發布你的代碼。node
package.jsonreact
{
...
"description": "webpack4-react",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
...
}
複製代碼
npm install --save lodash
複製代碼
或webpack
yarn add lodash
複製代碼
webpack4-react
|- package.json
|- /dist
|- index.html
|- /src
|- index.js
複製代碼
package.jsongit
{
"name": "webpack4-react",
"version": "1.0.0",
"description": "webpack4-react",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.15"
},
"devDependencies": {
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
}
}
複製代碼
dist/index.htmles6
<!DOCTYPE html>
<html>
<head>
<title>webpack4-react</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
複製代碼
src/index.jsgithub
import _ from 'lodash';
function component() {
var element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
document.body.appendChild(component());
複製代碼
npx webpack
將看到如下輸出:
Hash: 17a14a12467064d9d4dd
Version: webpack 4.41.5
Time: 1239ms
Built at: 2020-01-04 10:56:16
Asset Size Chunks Chunk Names
main.js 72.1 KiB 0 [emitted] main
Entrypoint main = main.js
[1] ./src/index.js 210 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
複製代碼
此時在 dist 文件夾下已經生成一個 main.js 文件 在瀏覽器中打開 dist 下的 index.html,若是一切訪問都正常,你應該能看到如下文本:'Hello webpack'。
簡易打包已經完成
webpack4-react
|- package.json
|- webpack.config.js
|- /dist
|- index.html
|- /src
|- index.js
複製代碼
webpack.config.js
const path = require('path');
module.exports = {
entry: {
//配置頁面入口
index: ['./src/index.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
dist/index.html
...
<body>
<script src="bundle.js"></script>
</body>
...
複製代碼
package.json
{
"name": "webpack4-react",
"version": "1.0.0",
"description": "webpack4-react",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.15"
},
"devDependencies": {
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
}
}
複製代碼
npm run build
複製代碼
終端將輸出:
Hash: 9cbb2fac6cc224bfe661
Version: webpack 4.41.5
Time: 1272ms
Built at: 2020-01-04 11:39:26
Asset Size Chunks Chunk Names
main.js 72.1 KiB 0 [emitted] main
Entrypoint main = main.js
[1] ./src/index.js 213 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
複製代碼
一樣的, dist 文件夾下生成 bundle.js 文件
這樣就實現了基本的 webpack 構建了
npm install --save-dev react react-dom
npm install --save-dev @babel/cli @babel/core @babel/preset-react @babel/preset-env @babel/plugin-transform-runtime babel-loader
src/index.js
import React from 'react';
import ReactDom from 'react-dom';
const hello = 'Hello React'
ReactDom.render(
<div>
<div>{hello}</div>
</div>,
document.getElementById('app'),
);
複製代碼
dist/index.html
...
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
...
複製代碼
爲了使用 babel 解析 jsx
webpack.config.js
...
entry: {
//配置頁面入口
index: ['./src/index.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 3,
targets: {
chrome: '58',
ie: '8',
},
},
],
'@babel/preset-react',
],
},
},
],
},
],
}
...
複製代碼
在根目錄下新建 .babelrc 文件
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3,
"targets": {
"chrome": "58",
"ie": "8"
}
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
複製代碼
當前文件目錄結構:
webpack4-react
|- /dist
|- index.html
|- /src
|- index.js
|- .babelrc
|- package.json
|- webpack.config.js
複製代碼
執行 npm run build
終端輸出:
```
$ webpack
Hash: f4d46dd4732764195f93
Version: webpack 4.41.5
Time: 446ms
Built at: 2020-01-04 13:28:48
Asset Size Chunks Chunk Names
bundle.js 128 KiB 0 [emitted] main
Entrypoint main = bundle.js
[3] ./src/index.js 211 bytes {0} [built]
+ 7 hidden modules
```
複製代碼
在瀏覽器中打開 dist 下的 index.html,若是一切訪問都正常,你應該能看到如下文本:'Hello React'。
webpack4-react
|- /src
|- index.js
|- index.html
|- .babelrc
|- package.json
|- webpack.config.js
複製代碼
src/index.html
<!DOCTYPE html>
<html>
<head>
<title>webpack4-react</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
複製代碼
npm install --save-dev webpack-dev-server
複製代碼
...
entry: {
//配置頁面入口
index: ['./src/index.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: '/src',
hot: true,
},
...
複製代碼
npm install --save-dev html-webpack-plugin
複製代碼
const HtmlWebpackPlugin = require('html-webpack-plugin');
...
devServer: {
contentBase: '/src',
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html',
chunks: ['index'],
inject: 'body',
}),
],
...
複製代碼
package.json
{
"name": "webpack4-react",
"version": "1.0.0",
"description": "webpack4-react",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --config webpack.config.js",
"build": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.15",
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"devDependencies": {
"@babel/cli": "^7.7.7",
"@babel/core": "^7.7.7",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.7.4",
"babel-loader": "^8.0.6",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
}
}
複製代碼
npm run dev
執行
npm run dev
後會自動打開瀏覽器,此時修改 index.js 文件中內容,瀏覽器會實時更新
刪除 dist 文件夾
執行 npm run build 打包依舊會在 dist 下生成打包文件
爲了從 JavaScript 模塊中 import 一個 CSS 文件,你須要在 module 配置中 安裝並添加 style-loader 和 css-loader:
npm install --save-dev style-loader css-loader
複製代碼
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
]
},
...
}
複製代碼
* {
padding: 0;
margin: 0;
}
div {
font-size: 20px;
}
複製代碼
src/index.jsimport './style/reset.css';
複製代碼
執行 npm run dev
,會看到 reset.css 中的樣式已經生效npm install --save-dev autoprefixer postcss-loader
npm install --save-dev less-loader node-sass sass sass-loader
複製代碼
這個過程當中安裝 node-sass 可能會很慢, 耐心等待
建立以下目錄文件及內容:
webpack4-react
|- src
|- components
|- Date
|- index.jsx
|- style.scss
|- style
|- reset.scss
|- index.js
|- .babelrc
|- package.json
|- webpack.config.js
複製代碼
src/components/Date/index.jsx
import React from 'react';
import style from './style.scss';
class DateComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date(),
};
}
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date(),
});
}
render() {
const { date } = this.state;
return (
<div>
<div className={style.title}>時間</div>
<div className={style.title}>
{date.toLocaleTimeString()}
</div>
</div>
);
}
}
export default DateComponent;
複製代碼
src/components/Date/style.scss
.title {
height: 50px;
font: bold 20px '微軟雅黑';
text-align: center;
color: #000;
}
複製代碼
src/index.js
import React from 'react';
import ReactDom from 'react-dom';
import DateComponents from './components/Date/index.jsx';
import './style/reset.scss';
const hello = 'Hello React';
ReactDom.render(
<div>
<div>{hello}</div>
<DateComponents />
</div>,
document.getElementById('app')
);
複製代碼
// css 配置
const styleLoader = {
loader: 'style-loader',
};
const cssLoader = {
loader: 'css-loader',
options: {
modules: true, // webpack3 爲 module
sourceMap: true,
importLoaders: 2,
},
};
const postCssLoader = {
loader: 'postcss-loader',
};
const sassLoader = {
loader: 'sass-loader',
options: {
sourceMap: true,
},
};
const lessLoader = {
loader: 'less-loader',
};
exports.loadersConfig = {
styleLoader,
cssLoader,
postCssLoader,
sassLoader,
lessLoader,
};
// css 配置
複製代碼
webpack.config.js
...
const utils = require('./tools/utils.js');
const {
postCssLoader,
styleLoader,
sassLoader,
cssLoader,
} = utils.loadersConfig;
...
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
exclude: /node_modules/,
use: [styleLoader, cssLoader, postCssLoader],
},
{
test: /\.scss$/,
include: [/pages/, /components/, /style/],
use: [
styleLoader,
cssLoader,
postCssLoader,
sassLoader,
],
},
}
...
};
複製代碼
const AUTOPREFIXER_BROWSERS = [
'Android 2.3',
'Android >= 4',
'Chrome >= 35',
'Firefox >= 31',
'Explorer >= 8',
'iOS >= 7',
'Opera >= 12',
'Safari >= 7.1',
];
module.exports = {
plugins: [require('autoprefixer')({overrideBrowserslist: ['> 0.15% in CN']})],
};
複製代碼
這時候執行
npm run dev
命令會報錯,由於缺乏一些 babel 依賴,下載一下就行了
npm install --save @babel/runtime core-js
複製代碼
執行 npm run dev
,自動打開瀏覽器,css 相關的配置構建完成
npm install --save-dev file-loader url-loader
複製代碼
...
module: {
rules: [
...
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
name: '[path][name].[ext]',
limit: 1024 * 15,
fallback: 'file-loader',
},
},
],
},
...
]
}
...
複製代碼
...
import reactLogo from './../../images/React.svg';
import webpackLogo from './../../images/webpack.svg';
...
render() {
const { date } = this.state;
return (
<div>
<div className={style.title}>時間</div>
<div>
<img className={style.logo} src={reactLogo} alt="" />
<img className={style.logo} src={webpackLogo} alt="" />
</div>
<div className={style.title}>
{date.toLocaleTimeString()}
</div>
</div>
);
}
...
複製代碼
字體、數據等參考 webpack 官網 資源管理
在代碼引入組件或圖片時,咱們來配置一些便捷的方式
webpack.config.js
// 引入 node 的 path 模塊
const path = require('path');
...
module.exports = {
...
resolve: {
// 設置模塊導入規則,import/require時會直接在這些目錄找文件
modules: ['node_modules'],
// import導入時省略後綴
extensions: ['.js', '.jsx', '.scss', '.less', '.css', '.json'],
// import導入時別名
alias: {
'@components': path.resolve('./src/components'),
'@images': path.resolve('./src/images'),
'@style': path.resolve('./src/style'),
},
},
...
}
複製代碼
舉個 🌰
src/index.js 中
import React from 'react';
import ReactDom from 'react-dom';
import DateComponents from '@components/Date/index.jsx';
import '@style/reset.scss';
const hello = 'Hello React';
ReactDom.render(
<div>
<div>{hello}</div>
<DateComponents />
</div>,
document.getElementById('app')
);
複製代碼
此時 執行 npm run dev
查看
開發環境(development)和生產環境(production)的構建目標差別很大。在開發環境中,咱們須要具備強大的、具備實時從新加載(live reloading)或熱模塊替換(hot module replacement)能力的 source map 和 localhost server。而在生產環境中,咱們的目標則轉向於關注更小的 bundle,更輕量的 source map,以及更優化的資源,以改善加載時間。因爲要遵循邏輯分離,咱們一般建議爲每一個環境編寫彼此獨立的 webpack 配置。
webpack-merge
配置npm install --save-dev webpack-merge clean-webpack-plugin uglifyjs-webpack-plugin
複製代碼
根目錄下建立 webpack 文件夾
|- webpack
|- webpack.common.js
|- webpack.dev.js
|- webpack.production.js
複製代碼
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const utils = require('./../tools/utils');
const { postCssLoader, styleLoader, sassLoader, cssLoader } = utils.loadersConfig;
module.exports = {
entry: {
//配置頁面入口
index: ['./src/index.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../dist'),
},
devServer: {
contentBase: '/src',
hot: true,
},
resolve: {
// 設置模塊導入規則,import/require時會直接在這些目錄找文件
modules: ['node_modules'],
// import導入時省略後綴
extensions: ['.js', '.jsx', '.scss', '.less', '.css', '.json'],
// import導入時別名
alias: {
'@assets': path.resolve('./src/assets'),
'@common': path.resolve('./src/common'),
'@components': path.resolve('./src/components'),
'@images': path.resolve('./src/images'),
'@pages': path.resolve('./src/pages'),
'@style': path.resolve('./src/style'),
},
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html',
chunks: ['index'],
inject: 'body',
}),
],
module: {
rules: [
{
test: /\.css$/,
exclude: /node_modules/,
use: [styleLoader, cssLoader, postCssLoader],
},
{
test: /\.scss$/,
include: [/pages/, /components/, /style/],
use: [styleLoader, cssLoader, postCssLoader, sassLoader],
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
name: '[path][name].[ext]',
limit: 1024 * 15,
fallback: 'file-loader',
},
},
],
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 3,
targets: {
chrome: '58',
ie: '8',
},
},
],
'@babel/preset-react',
],
},
},
],
},
],
},
};
複製代碼
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: '/src',
hot: true,
}
});
複製代碼
webpack.production.js
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); // 用來縮小(壓縮優化)js文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
plugins: [
new UglifyJSPlugin({
sourceMap: true,
}),
new CleanWebpackPlugin(),
],
});
複製代碼
"dev": "webpack-dev-server --open --config webpack/webpack.dev.js",
"build": "webpack --config webpack/webpack.production.js"
複製代碼
npm install --save-dev cross-env
複製代碼
npm 腳本命令更改
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --config webpack/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config webpack/webpack.production.js"
複製代碼
在 npm 腳本執行的時候設置的環境變量經過 process.env.NODE_ENV 來獲取,process.env.NODE_ENV 的值 在當前腳本下有兩種: development , production , 藉此能夠根據不一樣環境設置不一樣的配置。
npm install --save-dev html-loader
複製代碼
在根目錄下添加一個 react.ico 的圖片待用,用於在 HtmlWebpackPlugin 中配置網頁的 ico 圖片
webpack.config.common.js
...
entry: {
//配置頁面入口
index: ['./src/index.js'],
},
output: {
//配置輸出選項
path: path.resolve(__dirname, '../dist'), //輸出路徑爲,當前路徑下
filename: '[name].[hash:5].js', //輸出後的文件名稱
},
...
plugins: [
new HtmlWebpackPlugin({
title: 'webpack & react',
template: './src/index.html', //本地模板文件的位置,支持加載器(如handlebars、ejs、undersore、html等),如好比 handlebars!src/index.hbs;
filename: './index.html', //輸出文件的文件名稱,默認爲index.html,不配置就是該文件名;此外,還能夠爲輸出文件指定目錄位置(例如'html/index.html')
chunks: ['index'], // chunks主要用於多入口文件,當你有多個入口文件,那就回編譯後生成多個打包後的文件,那麼chunks 就能選擇你要使用那些js文件
inject: 'body', //一、true或者body:全部JavaScript資源插入到body元素的底部二、head: 全部JavaScript資源插入到head元素中三、false: 全部靜態資源css和JavaScript都不會注入到模板文件中
showErrors: true, //是否將錯誤信息輸出到html頁面中
hash: false, //是否爲全部注入的靜態資源添加webpack每次編譯產生的惟一hash值
favicon: 'react.ico', //添加特定的 favicon 路徑到輸出的 HTML 文件中。
minify: {
//是否對大小寫敏感,默認false
caseSensitive: true,
//是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫爲disabled 默認false
collapseBooleanAttributes: true,
//是否去除空格,默認false
collapseWhitespace: true,
//是否壓縮html裏的css(使用clean-css進行的壓縮) 默認值false;
minifyCSS: true,
//是否壓縮html裏的js(使用uglify-js進行的壓縮)
minifyJS: true,
//Prevents the escaping of the values of attributes
preventAttributesEscaping: true,
//是否移除屬性的引號 默認false
removeAttributeQuotes: true,
//是否移除註釋 默認false
removeComments: true,
//從腳本和樣式刪除的註釋 默認false
removeCommentsFromCDATA: true,
//是否刪除空屬性,默認false
removeEmptyAttributes: true,
// 若開啓此項,生成的html中沒有 body 和 head,html也未閉合
removeOptionalTags: false,
//刪除多餘的屬性
removeRedundantAttributes: true,
//刪除script的類型屬性,在h5下面script的type默認值:text/javascript 默認值false
removeScriptTypeAttributes: true,
//刪除style的類型屬性, type="text/css" 同上
removeStyleLinkTypeAttributes: true,
//使用短的文檔類型,默認false
useShortDoctype: true,
},
}),
]
...
module: {
rules: [
{
test: /\.html$/,
use: 'html-loader',
},
]
}
複製代碼
npm install --save-dev mini-css-extract-plugin
複製代碼
webpack.config.common.js 其中 關於 .scss$ 的 rules 替換下
...
const devMode = process.env.NODE_ENV !== 'production';
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
module.exports = {
...
plugins: [
...
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name]_[hash:5].css',
chunkFilename: devMode ? '[id].css' : '[id]_[hash:5].css',
disable: false, //是否禁用此插件
allChunks: true,
}),
...
]
...
module: {
rules: [
{
test: /\.html$/,
use: "html-loader"
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [styleLoader, cssLoader, postCssLoader]
},
{
test: /\.scss$/,
include: [/pages/, /components/, /style/],
use: [
devMode ? styleLoader : MiniCssExtractPlugin.loader,
cssLoader,
postCssLoader,
sassLoader,
],
},
],
}
複製代碼
npm run build
會發如今 dist 文件夾裏多了些 css,css.map 文件npm install --save-dev uglifyjs-webpack-plugin clean-webpack-plugin
複製代碼
webpack.production.js
...
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const common = require('./webpack.config.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
plugins: [
new UglifyJSPlugin({
sourceMap: true,
}),
new CleanWebpackPlugin(),
],
});
...
複製代碼
npm install --save-dev happypack
複製代碼
webpack.config.common.js
...
const os = require('os');
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
...
plugins: [
...
new HappyPack({
id: 'babel', //用id來標識 happypack處理那裏類文件
threadPool: happyThreadPool, //共享進程池
loaders: [
{
loader: 'babel-loader',
},
],
}),
...
],
...
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: ['happypack/loader?id=babel'],
exclude: /node_modules/, //設置node_modules裏的js文件不用解析
},
]
}
...
複製代碼
npm install --save-dev @babel/polyfill @babel/plugin-transform-arrow-functions @babel/preset-es2017
複製代碼
webpack.common.js
entry: {
//配置頁面入口
index: ['@babel/polyfill', './src/index.js'],
},
複製代碼
src/index.js
async function f() {
return 'hello world';
}
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
const hw = helloWorldGenerator();
console.log('Generator>>>>>>', hw.next());
const arr = [1, 2, 3, 4, 5, 1, 2, 3, 5];
const setArr = new Set(arr);
console.log('setArr?>>>>>>', setArr);
const m = new Map();
console.log('Map>>>>>>>>', m);
f().then(v => console.log('async>>>>', v));
// IE 不支持 Symbol
const helloSymbol = Symbol('www');
console.log('Symbol>>>>>>', helloSymbol);
console.log('flat>>>>>>', [1, [2, [3]]].flat(Infinity));
console.log('---------------');
console.log('Promise');
new Promise(resolve => {
setTimeout(() => {
resolve('hello');
}, 2000);
}).then(value => {
console.log(value);
return new Promise(resolve => {
setTimeout(() => {
resolve('world');
}, 2000);
});
}).then(value => {
console.log(`${value} world`);
});
console.log('---------------');
const target = {};
const handler = {};
const proxy = new Proxy(target, handler);
proxy.a = 'b';
console.log('proxy>>>>>', target.a);
// 不支持
// class A {
// static name = 'name';
// }
// console.log('static class>>>>>', new A());
複製代碼
打包 npm run build
, 把 dist 文件的 index.html 用 IE 打開驗證
vs code 格式化插件 使用的是 Prettier - Code formatter
以及 ESLint
|- .vscode
|- setting.json
複製代碼
setting.json
{
"editor.tabSize": 4,
"prettier.singleQuote": true,
"editor.detectIndentation": false,
"editor.renderControlCharacters": true,
"editor.renderWhitespace": "all",
"emmet.includeLanguages": {
"javascript": "javascriptreact"
},
"prettier.trailingComma": "es5",
"emmet.triggerExpansionOnTab": true,
"javascript.implicitProjectConfig.experimentalDecorators": true,
"workbench.colorTheme": "Solarized Light",
"window.zoomLevel": 0,
"prettier.useTabs": true,
"editor.foldingStrategy": "indentation",
"explorer.confirmDelete": false,
"javascript.updateImportsOnFileMove.enabled": "never",
"eslint.validate": [
{
"language": "javascript",
"autoFix": true
},
{
"language": "javascriptreact",
"autoFix": true
}
],
"eslint.autoFixOnSave": true
}
複製代碼
npm i babel-eslint eslint eslint-config-airbnb eslint-config-react-app eslint-friendly-formatter eslint-loader eslint-plugin-flowtype eslint-plugin-html eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react autoprefixer -D
複製代碼
根目錄下新建 .eslintrc.js 文件
module.exports = {
root: true,
env: {
browser: true,
commonjs: true,
es6: true,
},
extends: [
'airbnb',
],
globals: {
$: true,
process: true,
__dirname: true,
},
parser: 'babel-eslint',
parserOptions: {
//es6的module模式
sourceType: 'module',
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
ecmaVersion: 9,
},
settings: {
'import/ignore': ['node_modules', '.s?css', '@w*'],
},
// "excludedFiles": "*.test.js",
plugins: ['react', 'import', 'jsx-a11y'],
rules: {
'import/no-unresolved': 0,
'import/extensions': 0,
'import/prefer-default-export': 0,
'react/prop-types': 0,
'react/jsx-filename-extension': 0,
'react/prefer-stateless-function': 0,
'react/jsx-indent': [2, 'tab'],
'react/jsx-indent-props': [2, 'tab'],
'react/require-default-props': 0,
// // @off 同構應用須要在 didMount 裏寫 setState
'react/no-did-mount-set-state': 0,
'jsx-a11y/anchor-is-valid': 0,
'jsx-a11y/click-events-have-key-events': 0,
'jsx-a11y/mouse-events-have-key-events': 0,
'jsx-a11y/no-noninteractive-element-interactions': 0,
'jsx-a11y/no-static-element-interactions': 0,
'no-return-assign': 0,
'no-console': 0,
// 0、一、2分別表示不開啓檢查、警告、錯誤
indent: [2, 'tab', { SwitchCase: 1 }], // tab縮進
// 圈複雜度
complexity: [2, 9],
'max-params': [2, 7],
'max-depth': [2, 4],
'max-len': [
'error',
{
code: 150,
tabWidth: 4,
ignoreComments: true,
ignoreUrls: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
ignoreRegExpLiterals: true,
},
],
'no-tabs': 0,
'object-curly-newline': [
0,
{
ObjectExpression: 'always',
ObjectPattern: { multiline: true },
ImportDeclaration: 'never',
ExportDeclaration: {
multiline: true,
},
},
],
'object-curly-spacing': 0,
'arrow-parens': [2, 'as-needed'],
// 最大回調層數
'max-nested-callbacks': [2, 3],
'no-unused-vars': [
2,
{
argsIgnorePattern: '^React',
varsIgnorePattern: '[Rr]eact|[Ss]tyle',
},
],
'no-extra-boolean-cast': 0,
'array-callback-return': 0,
'no-param-reassign': 0,
'jsx-quotes': [0, 'prefer-double'], //強制在JSX屬性(jsx-quotes)中一導致用雙引號
'no-underscore-dangle': 0,
'quote-props': 0,
// "no-native-reassign": 2,//不能重寫native對象
// // if while function 後面的{必須與if在同一行,java風格。
// "brace-style": [2, "1tbs", { "allowSingleLine": true }],
// // 雙峯駝命名格式
// "camelcase": 2,
// // 以方括號取對象屬性時,[ 後面和 ] 前面是否須要空格, 可選參數 never, always
// "computed-property-spacing": [2,"never"],
// //容許箭頭函數能夠省略小括號
// 'arrow-parens': 0,
// 'no-extra-semi': 2, // 不容許多餘的分號
// //容許使用async-await函數
// 'generator-star-spacing': 0,
// //在開發環境開啓debugger功能,生產環境禁止使用debugger
// 'no-debugger': process.env.NODE_ENV === 'development' ? 0 : 2,
// "quotes": [2, "single"], //單引號
// "no-var": 2, //對var警告
// "semi": ["error", "always"], //不強制使用分號
// "no-irregular-whitespace": 0, //不規則的空白不容許
// "no-alert": 2, //禁止使用alert confirm prompt
// "no-lone-blocks": 0, //禁止沒必要要的嵌套塊
// "no-class-assign": 2, //禁止給類賦值
// "no-cond-assign": 2, //禁止在條件表達式中使用賦值語句
// "no-const-assign": 2, //禁止修改const聲明的變量
// "no-delete-var": 2, //不能對var聲明的變量使用delete操做符
// "no-dupe-keys": 2, //在建立對象字面量時不容許鍵重複
// "no-duplicate-case": 2, //switch中的case標籤不能重複
// "no-dupe-args": 2, //函數參數不能重複
// "no-empty": 2, //塊語句中的內容不能爲空
// "no-func-assign": 2, //禁止重複的函數聲明
// "no-invalid-this": 0, //禁止無效的this,只能用在構造器,類,對象字面量
// "no-redeclare": 2, //禁止重複聲明變量
// "no-spaced-func": 2, //函數調用時 函數名與()之間不能有空格
// "no-this-before-super": 0, //在調用super()以前不能使用this或super
// "no-undef": 2, //不能有未定義的變量
// "no-use-before-define": 2, //未定義前不能使用
// // "camelcase": 0, //強制駝峯法命名
// "no-mixed-spaces-and-tabs": 0, //禁止混用tab和空格
// "prefer-arrow-callback": 0, //比較喜歡箭頭回調
// "arrow-spacing": 0, //=>的前/後括號
//
// // 禁止在 componentDidMount 裏面使用 setState
// // 禁止在 componentDidUpdate 裏面使用 setState
// 'react/no-did-update-set-state': 2,
// // 禁止拼寫錯誤
// 'react/no-typos': 2,
// // 禁止使用字符串 ref
// 'react/no-string-refs': 2,
// // @fixable 禁止出現 HTML 中的屬性,如 class
// 'react/no-unknown-property': 2,
// // 禁止出現未使用的 propTypes
// // @off 不強制要求寫 propTypes
// 'react/no-unused-prop-types': 2,
// // 出現 jsx 的地方必須 import React
// // @off 已經在 no-undef 中限制了
// 'react/react-in-jsx-scope': 0,
// // 非 required 的 prop 必須有 defaultProps
// // @off 不強制要求寫 propTypes
// 'react/require-default-props': 0,
// // render 方法中必須有返回值
// 'react/require-render-return': 2,
// // @fixable 組件內沒有 children 時,必須使用自閉和寫法
// // @off 不必限制
// 'react/self-closing-comp': 0,
// // style 屬性的取值必須是 object
// 'react/style-prop-object': 2,
// // HTML 中的自閉和標籤禁止有 children
// 'react/void-dom-elements-no-children': 2,
// // 數組中的 jsx 必須有 key
// 'react/jsx-key': 2,
// // 禁止在 jsx 中使用像註釋的字符串
// 'react/jsx-no-comment-textnodes': 2,
// // 禁止出現重複的 props
// 'react/jsx-no-duplicate-props': 2,
// // 禁止使用未定義的 jsx elemet
// 'react/jsx-no-undef': 2,
// // jsx 文件必須 import React
// 'react/jsx-uses-react': 2,
// // 定義了的 jsx element 必須使用
// 'react/jsx-uses-vars': 2,
// // @fixable 多行的 jsx 必須有括號包起來
// // @off 不必限制
// 'react/jsx-wrap-multilines': 2,
// "react/no-array-index-key": 2, // 遍歷出來的節點必須加key
// "react/no-children-prop": 2, // 禁止使用children做爲prop
// "react/no-direct-mutation-state": 2, // 禁止直接this.state = 方式修改state 必須使用setState
},
};
複製代碼
webpack.common.js
...
module: {
rules: [
...
{
test: /\.(js|jsx)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [path.resolve(__dirname, 'src')], // 指定檢查的目錄
options: {
// 這裏的配置項參數將會被傳遞到 eslint 的 CLIEngine
formatter: require('eslint-friendly-formatter'), // 指定錯誤報告的格式規範
},
},
]
}
...
複製代碼
根目錄下新建 .eslintignore 文件 用來制定忽略某些文件的 eslint 校驗
webpack
複製代碼
npm i stylelint stylelint-config-recommended stylelint-config-standard stylelint-order stylelint-webpack-plugin -D
複製代碼
webpack.dev.js
const merge = require("webpack-merge");
const StyleLintPlugin = require("stylelint-webpack-plugin");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "development",
devtool: "inline-source-map",
devServer: {
contentBase: "/src",
hot: true
},
plugins: [
new StyleLintPlugin({
fix: true,
files: ["src/**/*.scss"],
failOnError: false,
quiet: true,
syntax: "scss",
cache: true
})
]
});
複製代碼
根目錄下新建 .stylelintrc.js 文件
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-recommended'],
plugins: ['stylelint-order'],
rules: {
'order/order': [
// "at-rules",
// "declarations",
'custom-properties',
'dollar-variables',
'rules',
],
'order/properties-order': [
'position',
'z-index',
'top',
'bottom',
'left',
'right',
'float',
'clear',
'columns',
'columns-width',
'columns-count',
'column-rule',
'column-rule-width',
'column-rule-style',
'column-rule-color',
'column-fill',
'column-span',
'column-gap',
'display',
'grid',
'grid-template-rows',
'grid-template-columns',
'grid-template-areas',
'grid-auto-rows',
'grid-auto-columns',
'grid-auto-flow',
'grid-column-gap',
'grid-row-gap',
'grid-template',
'grid-template-rows',
'grid-template-columns',
'grid-template-areas',
'grid-gap',
'grid-row-gap',
'grid-column-gap',
'grid-area',
'grid-row-start',
'grid-row-end',
'grid-column-start',
'grid-column-end',
'grid-column',
'grid-column-start',
'grid-column-end',
'grid-row',
'grid-row-start',
'grid-row-end',
'flex',
'flex-grow',
'flex-shrink',
'flex-basis',
'flex-flow',
'flex-direction',
'flex-wrap',
'justify-content',
'align-content',
'align-items',
'align-self',
'order',
'table-layout',
'empty-cells',
'caption-side',
'border-collapse',
'border-spacing',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'ruby-align',
'ruby-merge',
'ruby-position',
'box-sizing',
'width',
'min-width',
'max-width',
'height',
'min-height',
'max-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'border',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-color',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-image',
'border-image-source',
'border-image-slice',
'border-image-width',
'border-image-outset',
'border-image-repeat',
'border-top',
'border-top-width',
'border-top-style',
'border-top-color',
'border-top',
'border-right-width',
'border-right-style',
'border-right-color',
'border-bottom',
'border-bottom-width',
'border-bottom-style',
'border-bottom-color',
'border-left',
'border-left-width',
'border-left-style',
'border-left-color',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'outline',
'outline-width',
'outline-color',
'outline-style',
'outline-offset',
'overflow',
'overflow-x',
'overflow-y',
'resize',
'visibility',
'font',
'font-style',
'font-variant',
'font-weight',
'font-stretch',
'font-size',
'font-family',
'font-synthesis',
'font-size-adjust',
'font-kerning',
'line-height',
'text-align',
'text-align-last',
'vertical-align',
'text-overflow',
'text-justify',
'text-transform',
'text-indent',
'text-emphasis',
'text-emphasis-style',
'text-emphasis-color',
'text-emphasis-position',
'text-decoration',
'text-decoration-color',
'text-decoration-style',
'text-decoration-line',
'text-underline-position',
'text-shadow',
'white-space',
'overflow-wrap',
'word-wrap',
'word-break',
'line-break',
'hyphens',
'letter-spacing',
'word-spacing',
'quotes',
'tab-size',
'orphans',
'writing-mode',
'text-combine-upright',
'unicode-bidi',
'text-orientation',
'direction',
'text-rendering',
'font-feature-settings',
'font-language-override',
'image-rendering',
'image-orientation',
'image-resolution',
'shape-image-threshold',
'shape-outside',
'shape-margin',
'color',
'background',
'background-image',
'background-position',
'background-size',
'background-repeat',
'background-origin',
'background-clip',
'background-attachment',
'background-color',
'background-blend-mode',
'isolation',
'clip-path',
'mask',
'mask-image',
'mask-mode',
'mask-position',
'mask-size',
'mask-repeat',
'mask-origin',
'mask-clip',
'mask-composite',
'mask-type',
'filter',
'box-shadow',
'opacity',
'transform-style',
'transform',
'transform-box',
'transform-origin',
'perspective',
'perspective-origin',
'backface-visibility',
'transition',
'transition-property',
'transition-duration',
'transition-timing-function',
'transition-delay',
'animation',
'animation-name',
'animation-duration',
'animation-timing-function',
'animation-delay',
'animation-iteration-count',
'animation-direction',
'animation-fill-mode',
'animation-play-state',
'scroll-behavior',
'scroll-snap-type',
'scroll-snap-destination',
'scroll-snap-coordinate',
'cursor',
'touch-action',
'caret-color',
'ime-mode',
'object-fit',
'object-position',
'content',
'counter-reset',
'counter-increment',
'will-change',
'pointer-events',
'all',
'page-break-before',
'page-break-after',
'page-break-inside',
'widows',
],
indentation: 'tab',
'color-no-invalid-hex': true,
'font-family-no-missing-generic-family-keyword': null,
'font-family-name-quotes': null,
'function-url-quotes': 'always',
'at-rule-no-unknown': null,
'no-eol-whitespace': null,
'selector-attribute-quotes': 'always',
'string-quotes': 'single',
'selector-pseudo-element-colon-notation': null,
'at-rule-no-vendor-prefix': true,
'media-feature-name-no-vendor-prefix': null,
'media-feature-name-no-unknown': null,
'property-no-vendor-prefix': null,
'selector-no-vendor-prefix': true,
'value-no-vendor-prefix': true,
'selector-pseudo-class-no-unknown': null,
'shorthand-property-no-redundant-values': null,
'at-rule-empty-line-before': null,
'at-rule-name-space-after': null,
'comment-empty-line-before': null,
'declaration-bang-space-before': null,
'declaration-empty-line-before': null,
'function-comma-newline-after': null,
'function-name-case': null,
'function-parentheses-newline-inside': null,
'function-max-empty-lines': null,
'function-whitespace-after': null,
'number-leading-zero': null,
'number-no-trailing-zeros': null,
'rule-empty-line-before': null,
'selector-combinator-space-after': null,
'selector-list-comma-newline-after': null,
// "selector-pseudo-element-colon-notation": null,
'unit-no-unknown': null,
'no-descending-specificity': null,
'value-list-max-empty-lines': null,
},
};
複製代碼
在用 Webpack 打包的時候,對於一些不常常更新的第三方庫,好比 react,lodash,咱們並不但願每次打包都去編譯他們,因此,應該只打包一次,而後屢次使用,因而有了 DLL 的打包
下載依賴:
npm install webpack-bundle-analyzer --save-dev
複製代碼
webpack 文件夾下新建文件 webpack.dll.config.js
webpack.dll.config.js
const webpack = require('webpack');
const library = '[name]_lib';
const path = require('path');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
console.log('process.env.NODE_ENV>>>>', process.env.NODE_ENV)
module.exports = {
mode: 'production',
entry: {
vendors: [
'react',
'@babel/polyfill',
'react-dom',
'core-js',
'classnames'
],
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, './../ools'),
library,
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, './../tools/[name]-manifest.json'),
name: library,
}),
new BundleAnalyzerPlugin(),
],
};
複製代碼
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./tools/vendors-manifest.json'),
}),
]
複製代碼
"scripts": {
...
"dll": "cross-env NODE_ENV=production webpack --config webpack/webpack.dll.config.js",
...
},
複製代碼
執行 npm run dll
會在 tools 文件夾下生成對應的 dll 文件: vendors-manifest.json
和 vendors.dll.js
,同時會自動打開瀏覽器查看到對應文件大小