Webpack 4.0[2][4]

2-1

中文文档
讲师主页
印记中文

究竟是什么

最原始的网页开发方式

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>这是最原始的网页开发方式</title>
</head>
<body>
  <p>这是我们的网页内容</p>
  <div id="root"></div>
  <script src="./index.js"></script>
</body>
</html>
  • index.js
var dom = document.getElementById('root')

var header = document.createElement('div')
header.innerText = 'header'
dom.append(header)

var siderbar = document.createElement('div')
siderbar.innerText = 'siderbar'
dom.append(siderbar)

var content = document.createElement('div')
content.innerText = 'content'
dom.append(content)
  • 测试


面向对象的开发方式

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>这是面向对象的开发方式</title>
</head>
<body>
  <p>这是我们的网页内容</p>
  <div id="root"></div>
  <script src="./header.js"></script>
  <script src="./siderbar.js"></script>
  <script src="./content.js"></script>
  <script src="./index.js"></script>
</body>
</html>
  • header.js
function Header() {
  var header = document.createElement('div')
  header.innerText = 'header'
  dom.append(header)
}
  • sidebar.js
function Sidebar() {
  var siderbar = document.createElement('div')
  siderbar.innerText = 'siderbar'
  dom.append(siderbar)
}
  • content.js
function Content() {
  var content = document.createElement('div')
  content.innerText = 'content'
  dom.append(content)
}
  • index.js
var dom = document.getElementById('root')

new Header()
new Sidebar()
new Content()
  • 测试


模块化的开发方式(webpack)

  • cd lesson
  • npm init -y
  • npm install webpack-cli webpack --save-dev
> fsevents@1.2.11 install /Users/wutianyu/Desktop/lesson/node_modules/fsevents
> node-gyp rebuild

  SOLINK_MODULE(target) Release/.node
  CXX(target) Release/obj.target/fse/fsevents.o
  SOLINK_MODULE(target) Release/fse.node
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN lesson@1.0.0 No description
npm WARN lesson@1.0.0 No repository field.

+ webpack-cli@3.3.10
+ webpack@4.41.4
added 457 packages from 236 contributors in 15.191s
  • npx webpack index.js
  • 此命令新增了 webpack 翻译出来的文件,默认位置:./dist/main.js

上面模块化里面用到的文件,在下面👇统一列出

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模块化的开发方式</title>
</head>
<body>
  <p>这是我们的网页内容</p>
  <div id="root"></div>
  <script src="./dist/main.js"></script>
</body>
</html>
  • index.js
import Header from './header.js'
import Sidebar from './siderbar.js'
import Content from './content.js'

new Header()
new Sidebar()
new Content()
  • header.js
function Header() {
  var dom = document.getElementById('root')
  var header = document.createElement('div')
  header.innerText = 'header'
  dom.append(header)
}

export default Header
  • sidebar.js
function Sidebar() {
  var dom = document.getElementById('root')
  var siderbar = document.createElement('div')
  siderbar.innerText = 'siderbar'
  dom.append(siderbar)
}

export default Sidebar
  • content.js
function Content() {
  var dom = document.getElementById('root')
  var content = document.createElement('div')
  content.innerText = 'content'
  dom.append(content)
}

export default Content
  • 测试


2-2

是什么

  • 模块打包工具:bundler
  • webpack 支持下面所有的模块引入规范
    ES Module
    CommonJS
    CMD
    ADM

CommonJS 示例

  • npm install --save-dev @types/node
  • index.js
-import Header from './header.js'
-import Sidebar from './siderbar.js'
-import Content from './content.js'

+var Header = require('./header.js')
+var Sidebar = require('./siderbar.js')
+var Content = require('./content.js')

// ...
  • header.js
// ...

-export default Header
+module.exports = Header
  • sidebar.js
// ...

-export default Sidebar
+module.exports = Sidebar
  • content.js
// ...

-export default Content
+module.exports = Content

作业:读文档

2-3

安装 Node.js

  • mkdir webpack-demo
  • cd webpack-demo
  • npm init
  • package.json
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {},
  "author": "Mark",
  "license": "ISC"
}

安装 webpack

全局安装和卸载

  • npm install webpack-cli webpack -g
  • npm uninstall webpack-cli webpack -g
    项目内安装
  • cd webpack-demo
  • npm install webpack-cli webpack --save-dev
  • npx webpack -v
  • 查看可用版本 npm info webpack

2-4

使用默认配置文件

  • webpack.config.js
const path = require('path')

module.exports = {
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, 'bundle'),
    filename: 'bundle.js'
  }
}

不用再手动指定文件路径

  • npx webpack

自己配置的,要手动指定

  • npx webpack --config webpack.my.js

重整文件目录结构

  • webpack.config.js
const path = require('path')

module.exports = {
-  entry: './index.js',
+  entry: './src/index.js',
  output: {
-    path: path.resolve(__dirname, 'bundle'),
+    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
}
  • npx webpack
  • package.json
{
  "name": "lesson",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "bundle": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^12.12.21",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10"
  }
}
  • npm run bundle = npx webpack
  • ./index.html 移动到 ./dist/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模块化的开发方式</title>
</head>
<body>
  <p>这是我们的网页内容</p>
  <div id="root"></div>
-  <script src="./dist/main.js"></script>
+  <script src="./bundle.js"></script>
</body>
</html>
  • 测试


三种运行 webpack 的方式

  • webpack index.js
  • npx webpack index.js
  • npm run bundle -> webpack

作业:读文档

2-5

输出信息讲解

  • npm run bundle
> lesson@1.0.0 bundle /Users/wutianyu/Desktop/lesson
> webpack

Hash: 64df8628023b23900853
Version: webpack 4.41.4
Time: 70ms
Built at: 2019-12-20 17:14:17
    Asset      Size  Chunks             Chunk Names
bundle.js  1.36 KiB       0  [emitted]  main
Entrypoint main = bundle.js
[0] ./src/index.js 154 bytes {0} [built]
[1] ./src/header.js 186 bytes {0} [built]
[2] ./src/siderbar.js 196 bytes {0} [built]
[3] ./src/content.js 192 bytes {0} [built]
const path = require('path')

module.exports = {
+  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'file-loader'
      }
    }]
  }
}

3-1

Loader 是什么

可以加载图片的 file-loader

  • npm install file-loader -D
  • ./src/index.js
var Header = require('./header.js')
var Sidebar = require('./siderbar.js')
var Content = require('./content.js')
+var avatar = require('./avatar.jpg')

+console.log(avatar)

new Header()
new Sidebar()
new Content()
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
+  module: {
+    rules: [{
+      test: /\.(png|jpg|gif)$/,
+     use: {
+        loader: 'file-loader'
+      }
+   }]
+  }
}
  • npm run bundle
  • ./dist/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>file-loader</title>
</head>
<body>
  <div id="root"></div>
  <script src="./bundle.js"></script>
</body>
</html>
  • 测试


  • 删除 ./src/header.js && ./src/sidebar.js && ./src/content.js
  • ./src/index.js
import avatar from './avatar.jpg'

var img = new Image()
img.src = avatar

var root = document.getElementById('root')
root.append(img)
  • npm run bundle
  • 测试


3-2

打包的图片,路径和名字不变

  • npm run bundle
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'file-loader',
+        options: {
+          name: '[path][name].[ext]'
+        }
      }
    }]
  }
}

打包的图片,改变路径为 images

const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'file-loader',
        options: {
-          name: '[path][name].[ext]'
+          name: '[name].[ext]',
+          outputPath: 'images/'
        }
      }
    }]
  }
}

打包图片的另一种 loader

  • npm install url-loader -D
  • 最佳实践:2k以下,base64,节省一个请求
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
-        loader: 'file-loader',
+        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
+          limit: 2048
        }
      }
    }]
  }
}
  • npm run bundle

作业:读文档

3-3

样式处理

  • ./src/index.css
.avatar {
  width: 150px;
  height: 150px;
}
  • ./src/index.js
import avatar from './avatar.jpg'
+import './index.css'

var img = new Image()
img.src = avatar
+img.classList.add('avatar')

var root = document.getElementById('root')
root.append(img)
  • npm install style-loader css-loader -D
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
+    },{
+      test: /\.css$/,
+      use: ['style-loader', 'css-loader']
+    }]
  }
}
  • npm run bundle
  • 测试


下面的引入用相同的效果

  • ./src/index.css
-.avatar {
-  width: 150px;
-  height: 150px;
-}
+@import './avatar.css'
  • ./src/avatar.css
+.avatar {
+  width: 150px;
+  height: 150px;
+}

处理 sass

  • avatar.css
  • index.css -> index.scss
-@import './avatar.css'
+body {
+ .avatar {
+    width: 150px;
+    height: 150px;
+  }
+}
  • index.js
import avatar from './avatar.jpg'
-import './index.css'
+import './index.scss'

var img = new Image()
img.src = avatar
img.classList.add('avatar')

var root = document.getElementById('root')
root.append(img)
  • webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
-      test: /\.css$/,
+      test: /\.scss$/,
-      use: ['style-loader', 'css-loader']
+      use: ['style-loader', 'css-loader', 'sass-loader']
    }]
  }
}
  • npm install sass-loader node-sass --save-dev
  • npm run bundle
  • 测试


处理浏览器厂商前缀

  • index.scss
body {
  .avatar {
    width: 150px;
    height: 150px;
+    transform: translate(100px, 100px);
  }
}
  • webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader', 
        'css-loader', 
        'sass-loader',
+        'postcss-loader'
      ]
    }]
  }
}
  • npm i -D postcss-loader
  • npm install autoprefixer -D
  • postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}
  • npm run bundle
  • 测试


3-4

对 .scss 引用 .scss 的多级引用处理

  • webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader', 
-        'css-loader', 
+        {
+          loader: 'css-loader', 
+          options: {
+            importLoaders: 2
+          }
+        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  }
}

实现 CSS module,避免全局污染

  • webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2,
+            modules: true
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  }
}
  • createAvatar.js
import avatar from './avatar.jpg'

function createAvatar() {
  var img = new Image()
  img.src = avatar
  img.classList.add('avatar')

  var root = document.getElementById('root')
  root.append(img)
}

export default createAvatar
  • index.js
import avatar from './avatar.jpg'
-import './index.scss'
+import style from './index.scss'
import createAvatar from './createAvatar'

createAvatar()

var img = new Image()
img.src = avatar
-img.classList.add('avatar')
+img.classList.add(style.avatar)

var root = document.getElementById('root')
root.append(img)
  • npm run bundle
  • 测试


需要样式,主动引用,生效

  • createAvatar.js
import avatar from './avatar.jpg'
+import style from './index.scss'

function createAvatar() {
  var img = new Image()
  img.src = avatar
-  img.classList.add('avatar')
+  img.classList.add(style.avatar)

  var root = document.getElementById('root')
  root.append(img)
}

export default createAvatar
  • npm run bundle
  • 测试


处理字体文件

  • src/createAvatar.js
  • src/avatar.jpg
  • src/index.js
  • src/index.scss
  • 打开 iconfont
  • src/font/iconfont.eot
  • src/font/iconfont.svg
  • src/font/iconfont.ttf
  • src/font/iconfont.woff
  • src/font/iconfont.woff2
  • src/index.scss
@font-face {
  font-family: "iconfont";
  src: url('./font/iconfont.eot?t=1576845174515'); /* IE9 */
  src: url('./font/iconfont.eot?t=1576845174515#iefix') format('embedded-opentype'), /* IE6-IE8 */
  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPEAAsAAAAAB8wAAAN1AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqDCIJuATYCJAMQCwoABCAFhG0HSBvgBsiOkVscy/LL/yeCai179vZp/wOoQDFYLh+V8gQuSiRGx5gYn/IEwv1Pv60ejluwHVzVVoRsFOKAaCPX/RtV9T+HS5vi81uWyxyTo16A8dbAxh4gARfICfothzOvwqNrM4FiQm/EYX5pLTjKSK9A3IkCC44phVyTHppCvWRihngEO800UXEGwIP/+/iC6HAESa0CMvDoIk8Dma/Us3g04v8IzS4CmOPpIE6jwiKQiatSwwkUBhYhRW+utA00NSXGqvN5Fv//P2btRdCk//IIlUTUgaR2wCYOVrxSNpng1YevTBgPKRK8kAlNtHAPPAGRjI2zeJY6zkpHRx/KzZ+7dk11/Tpz9WrdlSutjFcb0JIPu3ZtoKpXnRupcq4l9VRD47RQco5/PbubmgxQgjxATjpHjGSYkf6BqXVbVsCIldTkwwTBMEIEM3IkqZo2BWqHNXUzOrsHLDkV6Ns0K0jwY2av3r61S93Oq73Pd16Mbt1CTvSSWzcXK5coYQBaghbfurXkwVR0/LhiKqVC1lQ0DR07jqYCFT7Ti4u+tbP+1g44wBdc7J57Ebv+V+dc06VX1Cmy1WL6deXvdJt8NDGCsMsKN/dQiS4tVElm8pNWRj4d4PM7sMvHLqHw13meRbF/cmwjoSAmlIN7hM/2Cnh+f+LmotuzrduCB/v5U76bA18UxTQu/K7rGzC+LN+nPA8sAN0raAvq7eP/owMIQiOrBjT01/2N/34tM9rcUr86UkqAl44P+oGtOzJDOyG8f2Zr5o9itszLBpsoc9Y4NsM0ZquJXhKKgt8TYdC9MUQjrVMJTSMxJA3jUGmaRGbsItS0bUJd0w4UCwqmtw2RIJGdgHljEYR+6yHp9QyVfmeRGXsXaka9hbr+QFAch9eCbdMhkjkSZmWsodUDaZEzm3imlskgvRoLfQ2slGT7OBWWbKKbjgqPzIYLsAlLYwyx9ROiZZmneclspPPRdthgMNMWyazDnByulWVLckQEX/amcM5sBNIBCcaSYRo0tYFoIo6ZCe8Wl5G571fDBH0ZsKSanDqlCiaxERunRQkX2QJZIDC1yrmWa2z6EUSTyXg0nsTMiJaPdJiBBsxolvJ2OhhHFk7bw2eRLAIX4tsKwucXGZ9wCRRkkRwKAhFKwg7KBWsSNKxOdO2KLNb5Flr04bSY04smAAAAAA==') format('woff2'),
  url('./font/iconfont.woff?t=1576845174515') format('woff'),
  url('./font/iconfont.ttf?t=1576845174515') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
  url('./font/iconfont.svg?t=1576845174515#iconfont') format('svg'); /* iOS 4.1- */
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.iconfangdajing:before {
  content: "\e60b";
}

.iconAa:before {
  content: "\e636";
}

.iconicon-checkin:before {
  content: "\e615";
}

  • src/index.js
var root = document.getElementById('root')
import './index.scss'

root.innerHTML = '<div class="iconfont iconfangdajing"></div>'
  • webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2,
-            modules: true
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
+    },{
+      test: /\.(eot|ttf|svg|woff)$/,
+      use: {
+        loader: 'file-loader',
+      }
+    }]
  }
}
  • npm run bundle
  • 测试


作业:读文档

3-5 plugins

自动生成 html

  • npm install -D html-webpack-plugin
  • npm run bundle
  • webpack.config.js
const path = require('path')
+var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
+  plugins: [new HtmlWebpackPlugin({
+    template: 'src/index.html'
+  })]
}
  • npm run bundle
  • 测试


每次打包都先清空上一次的

  • npm install clean-webpack-plugin -D
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [new HtmlWebpackPlugin({
    template: 'src/index.html'
+  }), new CleanWebpackPlugin()]
}

3-6 输入和输出

  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
-  // entry: './src/index.js',
+  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  output: {
    publicPath: 'https://cdn.com.cn',
    path: path.resolve(__dirname, 'dist'),
-    // filename: 'bundle.js'
+    filename: '[name].js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [new HtmlWebpackPlugin({
    template: 'src/index.html'
  }), new CleanWebpackPlugin()]
}

3-7 SourceMap 的配置

删除

  • ./src/font
  • ./src/index.scss
  • ./src/index.js
  • ./src/index.js
console.log('Hello, World!');
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
+  development devtool: 'cheap-module-eval-source-map',
+  production devtool: 'cheap-module-source-map',
  entry: {
    main: './src/index.js',
-    // sub: './src/index.js'
  },
  output: {
-    // publicPath: 'https://cdn.com.cn',
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [new HtmlWebpackPlugin({
    template: 'src/index.html'
  }), new CleanWebpackPlugin()]
}
  • npm run bundle

作业:读文档

3-8 webpack-dev-server

  • npm install webpack-dev-server -D
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [new HtmlWebpackPlugin({
    template: 'src/index.html'
  }), new CleanWebpackPlugin()],
+  devServer: {
+    contentBase: './dist',
+    open: true,
+    proxy: {
+      '/api': 'http://localhost:8000'
+    }
+  }
}
  • package.json
{
  "name": "lesson",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-    "bundle": "webpack",
+    "watch": "webpack --watch",
+    "start": "webpack-dev-server",
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.1"
  }
}

  • npm run start

扩展内容(自建服务器)

  • npm install express webpack-dev-middleware -D
  • package.json
{
  "name": "lesson",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",
    "start": "webpack-dev-server",
+    "server": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.1"
  }
}
  • server.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const config = require('./webpack.config.js')
const complier = webpack(config)

const app = express()
app.use(webpackDevMiddleware(complier, {}))

app.listen(3000, () => {
  console.log('server is running...')
})

作业

3-9 & 3-10

热更新 css

  • server.js
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
+var webpack = require('webpack')

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
+    },{
+      test: /\.css$/,
+      use: [
+        'style-loader',
+        'css-loader',
+        'postcss-loader'
+      ]
+    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
+    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
+    hot: true,
+    hotOnly: true,
-    proxy: {
-      '/api': 'http://localhost:8000'
-    }
  }
}
  • package.json
{
  "name": "lesson",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-    "bundle": "webpack",
-    "watch": "webpack --watch",
+    "start": "webpack-dev-server",
-    "server": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1"
  }
}

  • ./src/index.js
import './style.css'

var btn = document.createElement('button')
btn.innerHTML = '新增'
document.body.appendChild(btn)

btn.onclick = function () {
  var div = document.createElement('div')
  div.innerHTML = 'item'
  document.body.appendChild(div)
}
  • ./src/style.css
div:nth-of-type(odd) {
  background: blue;
}
  • npm run start

热更新 js

  • ./src/counter.js
function counter() {
  var div = document.createElement('div')
  div.setAttribute('id', 'counter')
  div.innerHTML = 1;
  div.onclick = function () {
    div.innerHTML = parseInt(div.innerHTML, 10) + 1
  }
  document.body.appendChild(div)
}

export default counter
  • number.js
function number() {
  var div = document.createElement('div')
  div.setAttribute('id', 'number')
  div.innerHTML = 3000
  document.body.appendChild(div)
}

export default number
  • ./src/index.js
  • ./src/index.js
import counter from './counter'
import number from './number'

counter()
number()

if (module.hot) {
  module.hot.accept('./number', () => {
    document.body.removeChild(document.getElementById('number'))
    number()
  })
}

作业:读文档

3-11

  • ./src/counter.js
  • ./src/number.js
  • ./src/style.css
  • ./src/index.js
  • ./src/index.js
const arr = [
  new Promise(() => {}),
  new Promise(() => {})
]

arr.map(item => {
  console.log(item)
})
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
var webpack = require('webpack')

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
+      test: /\.js$/, 
+      exclude: /node_modules/, 
+      loader: "babel-loader",
+      options: {
+        "presets": [['@babel/preset-env', {
+          corejs: '2.6.11',
+          useBuiltIns: 'usage'
+        }]]
+      }
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true,
    hotOnly: true,
  }
}
  • npm install --save-dev babel-loader @babel/core
  • npm install @babel/preset-env --save-dev
  • npm install --save @babel/polyfill
  • npx webpack
  • babel官网

打包组件库

  • npm install --save-dev @babel/plugin-transform-runtime
  • npm install --save @babel/runtime
  • npm install --save @babel/runtime-corejs2
  • .babelrc(options)
{
        "plugins": [["@babel/plugin-transform-runtime", {
//          "absoluteRuntime": false,
          "corejs": 2,
          "helpers": true,
          "regenerator": true,
          "useESModules": false,
//          "version": "7.0.0-beta.0"
        }]]
}

3-13

  • npm install react react-dom --save
  • npm install --save-dev @babel/preset-react
  • ./src/index.js
  • ./src/index.js
import React, { Component } from 'react'
import ReactDom from 'react-dom'

class App extends Component {
  render() {
    return (
      <div>Hello World!</div>
    )
  }
}

ReactDom.render(<App />, document.getElementById('root'))
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
var webpack = require('webpack')

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
// -      options: {
// -        "presets": [['@babel/preset-env', {
// -          corejs: '2.6.11',
// -          useBuiltIns: 'usage'
// -        }]]
// -      }
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true,
    hotOnly: true
  }
}
  • .babelrc
{
  "presets": [
    [
      "@babel/preset-env", {
        "corejs": "2.6.11",
        "useBuiltIns": "usage"
      }
    ],
    "@babel/preset-react"
  ]
}

react 热加载(webpack)

  • ./src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

const rootEl = document.getElementById("root")

ReactDOM.render(
    <App />,
    rootEl
)

if(module.hot) {
    module.hot.accept("./App", () => {
        const App = require("./App").default;
    
        ReactDOM.render(
            <App />,
            rootEl
        )
    })
}
  • ./src/app.js
import React, { Component } from 'react'

class App extends Component {
  render() {
    return (
      <div>Hello World</div>
    )
  }
}

export default App

没有测试通过

  • npm install react-hot-loader
  • npm install @types/react

4-1 Tree Shaking

只有引用的方法会被打包(生产)

  • ./src/index.js
  • ./src/index.js
import { add } from './math.js'

add(1, 2)
  • ./src/math.js
export const add = (a, b) => {
  console.log(a + b);
}

export const minus = (a, b) => {
  console.log(a - b);
}
  • package.json
{
  "name": "lesson",
+  "sideEffects": false,
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack-dev-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.7.7",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1"
  },
  "dependencies": {
    "@babel/polyfill": "^7.7.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}

  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
var webpack = require('webpack')

module.exports = {
-  mode: 'development',
+ mode: 'production',
-  devtool: 'cheap-module-eval-source-map',
+ devtool: 'cheap-module-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true,
    hotOnly: true
  },
-  optimization: {
-    usedExports: true
-  }
}

4-2

将配置文件分成开发和线上

  • webpack.prod.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
var webpack = require('webpack')

module.exports = {
-  mode: 'development',
+  mode: 'production',
-  devtool: 'cheap-module-eval-source-map',
+  devtool: 'cheap-module-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
-    new webpack.HotModuleReplacementPlugin()
  ],
-  devServer: {
-    contentBase: './dist',
-    open: true,
-    port: 8080,
-    hot: true,
-    hotOnly: true
-  },
-  optimization: {
-    usedExports: true
-  }
}
  • package.json
{
  "name": "lesson",
  "sideEffects": false,
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-    "start": "webpack-dev-server",
+    "dev": "webpack-dev-server --config webpack.dev.js",
+    "build": "webpack --config webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.7.7",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1"
  },
  "dependencies": {
    "@babel/polyfill": "^7.7.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}
  • webpack.config.js
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin')
var webpack = require('webpack')

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true,
-    hotOnly: true
  },
  optimization: {
    usedExports: true
  }
}

抽取公共部分

  • npm install webpack-merge -D
  • webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ]
}
  • webpack.dev.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')

const devConfig = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true
  },
  optimization: {
    usedExports: true
  }
}

module.exports = merge(commonConfig, devConfig)
  • webpack.prod.js
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')

const prodConfig = {
  mode: 'production',
  devtool: 'cheap-module-source-map'
}

module.exports = merge(commonConfig, prodConfig)

4-3

  • package.json
{
  "name": "lesson",
  "sideEffects": false,
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-    "dev": "webpack-dev-server --config webpack.dev.js",
-    "build": "webpack --config webpack.prod.js"
+    "dev-build": "webpack --config ./build/webpack.dev.js",
+    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
+    "build": "webpack --config ./build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.7.7",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1",
    "webpack-merge": "^4.2.2"
  },
  "dependencies": {
    "@babel/polyfill": "^7.7.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}
  • webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
-    path: path.resolve(__dirname, 'dist'),
+    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ]
}

进入正题,代码拆分

  • npm install lodash --save
  • ./build/webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
+    lodash: './src/lodash.js',
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ]
}
  • ./src/loads.js
import _ from 'lodash'
window._ = _
  • ./src/index.js
console.log(_.join(['a', 'b', 'c'], '***'))

同步加载,代码分割

  • ./src/lodash.js
  • ./src/index.js
  • ./src/index.js
import _ from 'lodash'

console.log(_.join(['a', 'b', 'c'], '***'))
  • webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
-    lodash: './src/lodash.js',
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
+  optimization: {
+    splitChunks: {
+      chunks: 'all'
+    }
+  }
}

异步加载,代码分割

  • npm install babel-plugin-dynamic-import-webpack -D
  • ./src/index.js
function getComponent() {
  return import('lodash').then(({ default: _ }) => {
    var element = document.createElement('div')
    element.innerHTML = _.join(['Dell', 'Lee'], '_')
    return element
  })
}

getComponent().then(element => {
  document.body.appendChild(element)
})
  • .babelrc
{
  "presets": [
    [
      "@babel/preset-env", {
        "corejs": "2.6.11",
        "useBuiltIns": "usage"
      }
    ],
    "@babel/preset-react"
  ],
+  "plugins": ["dynamic-import-webpack"]
}

4-5

改名

  • npm install --save-dev @babel/plugin-syntax-dynamic-import
  • ./src/index.js
function getComponent() {
  return import(/* webpackChunkName:"lodash" */'lodash').then(({ default: _ }) => {
    var element = document.createElement('div')
    element.innerHTML = _.join(['Dell', 'Lee'], '_')
    return element
  })
}

getComponent().then(element => {
  document.body.appendChild(element)
})

./babelrc

{
  "presets": [
    [
      "@babel/preset-env", {
        "corejs": "2.6.11",
        "useBuiltIns": "usage"
      }
    ],
    "@babel/preset-react"
  ],
-  "plugins": ["dynamic-import-webpack"]
+  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}
  • package.json
{
  "name": "lesson",
  "sideEffects": false,
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev-build": "webpack --config ./build/webpack.dev.js",
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.7.7",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "babel-loader": "^8.0.6",
-    "babel-plugin-dynamic-import-webpack": "^1.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1",
    "webpack-merge": "^4.2.2"
  },
  "dependencies": {
    "@babel/polyfill": "^7.7.0",
    "lodash": "^4.17.15",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}
  • ./build/webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
+      cacheGroups: {
+        vendors: false,
+        default: false
+      }
    }
  }
}
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,// 超过 30K 分割
      minChunks: 1,// 一个模块被引用几次才分割
      maxAsyncRequests: 5,//同时加载的模块数最多5个
      maxInitialRequests: 3,//首页引入的库最多3个
      automaticNameDelimiter: '~',
      name: true,//cacheGroups里的名字有效
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          filename: 'vendors.js'
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
          filename: 'common.js'
        }
      }
    }
  }
  • ./src/index.js
import _ from 'lodash'

var element = document.createElement('div')
element.innerHTML = _.join(['Dell', 'Lee'], '_')
document.body.appendChild(element)

// function getComponent() {
//   return import(/* webpackChunkName:"lodash" */'lodash').then(({ default: _ }) => {
//     var element = document.createElement('div')
//     element.innerHTML = _.join(['Dell', 'Lee'], '_')
//     return element
//   })
// }

// getComponent().then(element => {
//   document.body.appendChild(element)
// })

4-7

懒加载

  • ./src/index.js
//- import _ from 'lodash'

//- var element = document.createElement('div')
//- element.innerHTML = _.join(['Dell', 'Lee'], '_')
//- document.body.appendChild(element)
// 懒加载主要是下面的promise import 方式
function getComponent() {
  return import(/* webpackChunkName:"lodash" */'lodash').then(({ default: _ }) => {
    var element = document.createElement('div')
    element.innerHTML = _.join(['Dell', 'Lee'], '_')
    return element
  })
}
// 鼠标点击才加载 lodash 库
+document.addEventListener('click', () => {
+  getComponent().then(element => {
+    document.body.appendChild(element)
+  })
+})

//- getComponent().then(element => {
//-   document.body.appendChild(element)
//- })

ES7 的语法改写

  • ./src/index.js
async function getComponent() {
  const { default: _ } = await import(/* webpackChunkName:"lodash" */'lodash')
  const element = document.createElement('div')
  element.innerHTML = _.join(['Dell', 'Lee'], '_')
  return element
}

chunk 是什么

  • 代码分割,默认即可
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
  • ./build/webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
}

4-8 打包代码分析*****

  • package.json
+"dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"
内容:对打包过程的描述

https://webpack.docschina.org/guides/code-splitting/#bundle-分析-bundle-analysis-

https://webpack.docschina.org/guides/code-splitting/#预取-预加载模块-prefetch-preload-module-

4-9

css 的代码分割

  • npm install --save mini-css-extract-plugin
  • ./build/webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js',
+    chunkFilename: '[name].chunk.js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },
    //- {
    //-   test: /\.css$/,
    //-   use: [
    //-     'style-loader',
    //-     'css-loader',
    //-     'postcss-loader'
    //-   ]
    //- },{
    //-   test: /\.scss$/,
    //-   use: [
    //-     'style-loader',
    //-     {
    //-       loader: 'css-loader', 
    //-       options: {
    //-         importLoaders: 2
    //-       }
    //-     },
    //-     'sass-loader',
    //-     'postcss-loader'
    //-   ]
    //- },
    {
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  optimization: {
+    usedExports: true
    splitChunks: {
      chunks: 'all'
    }
  }
}
  • ./build/webpack.dev.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')

const devConfig = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
+  module: {
+    rules: [{
+      test: /\.css$/,
+      use: [
+        'style-loader',
+        'css-loader',
+        'postcss-loader'
+      ]
+    },{
+      test: /\.scss$/,
+      use: [
+        'style-loader',
+        {
+          loader: 'css-loader', 
+          options: {
+            importLoaders: 2
+          }
+        },
+        'sass-loader',
+        'postcss-loader'
+      ]
+    }]
+  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true
  },
-  optimization: {
-    usedExports: true
-  }
}

module.exports = merge(commonConfig, devConfig)
  • ./build/webpack.prod.js
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const prodConfig = {
  mode: 'production',
  devtool: 'cheap-module-source-map',
+  plugins: [
+    new MiniCssExtractPlugin({
+      filename: "[name].css",
+      chunkFilename: "[id].css"
+    })
+  ],
+  module: {
+    rules: [{
+      test: /\.css$/,
+      use: [
+        MiniCssExtractPlugin.loader,
+        'css-loader',
+        'postcss-loader'
+      ]
+    },{
+      test: /\.scss$/,
+      use: [
+        MiniCssExtractPlugin.loader,
+        {
+          loader: 'css-loader', 
+          options: {
+            importLoaders: 2
+          }
+        },
+        'sass-loader',
+        'postcss-loader'
+      ]
+    }]
+  }
}

module.exports = merge(commonConfig, prodConfig)
  • package.json
{
  "name": "lesson",
-  "sideEffects": false,
+  "sideEffects": [
+    "*.css"
+  ],
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev-build": "webpack --config ./build/webpack.dev.js",
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.7.7",
    "@babel/plugin-syntax-dynamic-import": "^7.7.4",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.9.0",
    "node-sass": "^4.13.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1",
    "webpack-merge": "^4.2.2"
  },
  "dependencies": {
    "@babel/polyfill": "^7.7.0",
    "lodash": "^4.17.15",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}

合并

  • npm install --save-dev optimize-css-assets-webpack-plugin
  • webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')


module.exports = {
  entry: {
    main: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js',
    chunkFilename: '[name].chunk.js'
  },
  module: {
    rules: [{ 
      test: /\.js$/, 
      exclude: /node_modules/, 
      loader: 'babel-loader'
    },{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    },{
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader',
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  optimization: {
    usedExports: true,
    splitChunks: {
      chunks: 'all'
    }
  }
}
  • webpack.dev.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')

const devConfig = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true
  }
}

module.exports = merge(commonConfig, devConfig)
  • webpack.prod.js
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")

const prodConfig = {
  mode: 'production',
  devtool: 'cheap-module-source-map',
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ],
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader',
        'postcss-loader'
      ]
    },{
      test: /\.scss$/,
      use: [
        MiniCssExtractPlugin.loader,
        {
          loader: 'css-loader', 
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  optimization: {
    minimizer: [
      new OptimizeCSSAssetsPlugin({})
    ]
  }
}

module.exports = merge(commonConfig, prodConfig)
  • package.json
{
  "name": "lesson",
  "sideEffects": [
    "*.css"
  ],
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev-build": "webpack --config ./build/webpack.dev.js",
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.7.7",
    "@babel/plugin-syntax-dynamic-import": "^7.7.4",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "@types/node": "^12.12.21",
    "autoprefixer": "^9.7.3",
    "babel-loader": "^8.0.6",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.4.0",
    "express": "^4.17.1",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.9.0",
    "node-sass": "^4.13.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.4",
    "webpack-cli": "^3.3.10",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "^3.10.1",
    "webpack-merge": "^4.2.2"
  },
  "dependencies": {
    "@babel/polyfill": "^7.7.0",
    "lodash": "^4.17.15",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352