Webpack的基本使用

一项目初始化

1. init初始化

  • 使用npm init -y完成项目初始化, 会生成 package.json 文件
    • init: 完成项目初始化
    • -y: 使用默认选项

2. 安装webpack

  • 在命令行中执行npm install webpack webpack-cli -D
    • -D: 安装开发环境依赖, 是–save-dev 的简写

二 编写项目入口

1. 编写index.html

在项目根目录下创建一个 index.html, 作为项目主页文件, 编写如下内容

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>

2. 编写vue根实例

创建 src 目录. 在 src 目录下创建 main.js 作为项目的总入口文件

在 main.js 中, 完成如下操作

  1. 创建 vue 根实例
  2. 挂载 App 组件
1) 安装vue

执行npm install vue安装 Vue

2) 创建Vue根实例

编写main.js

// 从vue包中导入Vue对象
import Vue from 'vue'

// 创建Vue根实例
new Vue({
  el: '#app'
})
3) 挂载App组件
  • step1: 编写App.vue文件(src下)

    <template>
      <div>this is App</div>
    </template>
    
    <script>
    export default {
      name: 'App'
    }
    </script>
    
    <style scoped></style>
    
  • step2: 挂载到根实例

    在 main.js 中, 完成如下操作

    1. 导入 App 组件对象
    2. 挂载到 Vue 根实例
    // 从vue包中导入Vue对象
    import Vue from 'vue'
    
    // 导入App组件对象
    import App from './App.vue'
    
    // 创建Vue根实例
    new Vue({
      el: '#app',
      components: {
        // 组件名: 组件对象
        App: App
      }
    })
    
4) 使用App标签渲染
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

3. 引入main.js测试

在 index.html 主页文件中引入 main.js, 测试, 发现报错!!!

Uncaught SyntaxError: Unexpected identifier

原因: 浏览器是不认识import这些东西的,因此需要使用webpack完成打包

三 webpack基本配置

为了让浏览器能够正确的解析, 我们需要使用 webpack 将我们的源代码进行打包

1. 创建webpack配置文件

在根目录下创建 webpack 配置文件 webpack.config.js, 编写最基本的配置

// 使用node的path模块
const path = require('path')

module.exports = {
  // 打包的入口
  entry: './src/main.js',
  // 打包的出口
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

2. 编写webpack脚本

在 packpage.json 中, 创建一个执行脚本: "build": "webpack"

这样就可以通过npm run build来执行 webpack 打包了

3. 测试

在命令行中执行npm run build

会发现提示这个错误

ERROR in ./src/App.vue 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

原因是: webpack 只能打包 js 文件. 对于后缀名为 vue 的文件不能打包. 如何解决 呢?

通过 vue-loader 来打包 vue 文件

四 使用vue-loader打包vue文件

vue-loader 的官网: https://vue-loader.vuejs.org/zh/guide/#vue-cli

1. 安装vue-loader

  • npm install -D vue-loader vue-template-compiler
  • 安装完成后, 发现 vue-loader 依赖 css-loader, 因此我们也要手动安装一下 css-loader

requires a peer of css-loader@* but none is installed. You must install peer dependencies yourself.

2. 安装css-loader

  • npm install -D css-loader

3. webpack配置

从 vue-load@15 版本开始, vue-loader 需要在 webpack 中添加一个插件

// 使用node的path模块
const path = require('path')

// 引入vue-loader插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  // 打包的入口
  entry: './src/main.js',
  // 打包的出口
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  // 打包规则
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin()
  ]
}

4. 重新打包测试

重新打包, 在 dist 目录下就会生成 bundle.js

我们发现在 webpack 打包时会出现这个警告

WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

解决: 找到 webpack.config.js 在配置中添加 mode

mode: 'development',

5. 在主页中引入bundle.js

在 index.html 中引入 bundle.js 测试, 发现报错

You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

原因:

Vue 会打包生成三个文件:

  • runtime only 的文件 vue.common.js
  • compiler only 的文件 compiler.js
  • runtime + compiler 的文件 vue.js

解决:

在 webpack 中, 添加别名的配置

resolve: {
    alias: {
      'vue': 'vue/dist/vue.js'
    }
  },

重新打包,测试

总结:

  1. webpack 本身只能打包 js 文件, 如果要打包其他文件就需要借助于 loader
  2. loader 其实就是专门用于打包特定文件的处理程序

五 其他常用loader

一般来说, 一个前端项目除了 js 文件外, 还有一些常用的文件, 如图片文件css文件

对于这些文件, webpack 都不会打包, 需要我们安装对应的 loader 帮助 webpack 打包

1. 打包图片文件

1) file-loader

file-loader: 将文件复制到对应的路径, 并返回文件名

官方文档: https://www.webpackjs.com/loaders/file-loader/

安装file-loader
  • npm install -D file-loader
配置
  • 修改 webpack 配置, 添加一条规则

    module: {
      rules: [
        {
          test: /\.(jpg|jpeg|png|svg)$/,
          loader: 'file-loader'
        }
      ]
    }
    
  • test: 正则表达式–如果需要打包的文件以 jpg 或者 jpeg 或者 png 或者 svg 结尾时

  • loader: 使用 file-loader

测试
  • 在 src 目录下创建 assets 目录, 存放静态资源文件(如: /images/todolist/styles/fonts 等)
  • 在images下放一张图片
  • 在 App.vue 中导入图片, 重新打包, 会在 dist 目录下生成我们需要的图片

原理:当遇到 jpg 结尾的文件时, 使用 file-loader 将文件 copy 到 dist 目录下. 文件名是图片的 hash 值

e389f60ab57f21e4162c67eb935a91f3.jpg 22.2 KiB [emitted]

如果希望保留原有的文件名, 可以使用占位符(placeholder)配置

{
  test: /\.(jpg|jpeg|png|svg)$/,
  loader: 'file-loader',
  options: {
    name: '[name].[ext]'
  }
}

结果:

avatar.jpg 22.2 KiB [emitted]

2) url-loader

url-loader: 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。

好处是: 直接将小图片打包以 base64 打包在 js 中, 减少 Http 请求的次数, 提高访问效率

安装url-loader
  • npm install -D url-loader
    

由于 url-loader 依赖 file-loader, 因此安装 url-loader 需要先安装 file-loader, 一般来说执行如下命令更适合

  • npm install -D file-loader url-loader
    
配置
module: {
  rules: [
    {
      test: /\.(jpg|jpeg|png|svg)$/,
      loader: 'url-loader',
      options: {
        name: '[name].[ext]',
        limit: 2048
      }
    }
  ]
}
  • test: 正则表达式–如果需要打包的文件以 jpg 或者 jpeg 或者 png 或者 svg 结尾时
  • loader: 使用 url-loader
  • options: 选项
    • limit: 当文件小于 2048byte 时, 以 base64 打包到 js 中, 当文件大于 2048byte 时, 使用 file-loader 打包
    • name: 打包的文件名使用"源文件名.扩展名"方式

2. 打包css文件

css 文件是前端项目必不可少的文件, webpack 通过 css-loader 和 style-loader 来打包 css 文件

1) 安装
安装css-loader

在安装 vue-loader 时, 我们已经安装了 css-loader. 可以通过查看package.json确认

安装style-loader
  • npm install -D style-loader
    
2) 配置
module: {
  rules: [{
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }]
}
  • use: 使用两个 loader: style-loader 和 css-loader
  • 这里的书写顺序是有讲究的, 按照从右到左, 从下到上的顺序依次执行
3) 测试
  • 在 styles 目录下创建 test.css 文件, 编写样式
  • 在App.vue中导入test.css
  • npm run build
4) 作用

css-loader: 解决文件之间的依赖关系, 把所有的 css 文件打包成一个文件

style-loader: 将 css-loader 打包完成后生成的文件挂载到页面的 head 标签的 style 中

比如, 在实际项目中一般会把初始化样式独立出来, 再通过 import 引入

css-loader 为分析 css 文件之间的引用关系, 最终形成一个整体文件

再通过 style-loader 将 css-loader 的内容挂载到页面的 head 的 style 标签中

六 插件

什么是插件?

在某个时间点,自动执行的处理程序(类似于vue的生命周期函数)

1. html-webpack-plugin

作用: 在打包结束时, 在 dist 目录下自动生成 index.html 文件, 并把打包好的 js 文件引入到 html 中

1) 安装

npm install -D html-webpack-plugin

2) 配置
const HtmlWebpckPlugin = require('html-webpack-plugin')

  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpckPlugin()
  ]
3) 测试
  • 报错: Cannot find element: #app
4) 指定模板

其实, 我们是可以指定生成 html 时, 以哪个文件为模板的

修改webpack配置

plugins: [
  new HtmlWebpackPlugin({
    template: './index.html'
  })
]

2. clean-webpack-plugin

1) 安装

npm install -D clean-webpack-plugin

2) 配置
const { CleanWebpackPlugin }  = require('clean-webpack-plugin')

plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin(),
    new HtmlWebpckPlugin({
      template: './index.html'
    }),
    new CleanWebpackPlugin()
  ]

3. autoprefixer

自动添加厂商前缀

1) 安装

autoprefixer 插件是 postcss-loader 提供的一个插件, 如果要使用这个插件, 我们得先安装 postcss-loader

npm install -D postcss-loader autoprefixer

2) 配置
  • webpack配置

    {
      test: /\.styl(us)?$/,
        use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'stylus-loader']
    }
    
  • 新建postcss.config.js

    module.exports = {
      plugins: [require('autoprefixer')]
    }
    

七 开发环境

1. devServer

webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。

只要代码修改后, webpack 会自动重新打包, 页面会重新刷新加载. 这样就不需要每次执行打包命令了

1) 配置devServer
安装

npm install -D webpack-dev-server

基本配置
  • webpack配置

    // devServer配置
    devServer: {
      // 指定服务器根目录
      contentBase: './dist',
      // 自动打开浏览器
      open: true
    },
    
  • 在package.json中添加一个脚本

    "scripts": {
      "start": "webpack-dev-server"
    },
    
2)其他常见配置
  • host: 服务器主机
  • port: 端口
  • open: 打开浏览器
  • hot: 热模块替换
  • proxy: 代理

2. 热模块替换

HMR不适用于生产环境

1) 启用
  • 配置

    devServer: {
      // 指定服务器根目录
      contentBase: './dist',
      // 自动打开浏览器
      open: true,
      // 启用热模块替换
      hot: true
    },
    
  • 插件

    const webpack = require('webpack')
    
    new webpack.HotModuleReplacementPlugin()
    
2) 测试
<template>
  <div>
    <h1>TodoList</h1>
    <input type="text" v-model="content" />
    <button @click="addTodo">添加</button>
    <ul>
      <li v-for="item of todoData" :key="item.id">{{ item }}</li>
    </ul>
  </div>
</template>

<script>
// import './assets/styles/global.styl'

export default {
  name: 'App',
  data() {
    return {
      todoData: ['todo1', 'todo2', 'todo3'],
      content: ''
    }
  },
  methods: {
    addTodo() {
      if (this.content === '') return

      this.todoData.push(this.content)
      this.content = ''
    }
  }
}
</script>

<style lang="stylus" scoped>
li:nth-of-type(odd)
  color: red
</style>

修改颜色, 发现之前添加的内容依然存在

3. SourceMap

  • SourceMap: (源代码映射) 建立打包后的文件和源代码所在行的映射,
    • 主要作用: 在开发时快速定位到出错的源代码行
  • devtool
    • none用于生产环境
    • cheap-module-eval-source-ma用于开发环境

八 生产环境

开发环境(development)生产环境(production) 的构建目标差异很大。

  • 开发环境 中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。
  • 生产环境 中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间

因此, 配置会有所不同, 官方推荐使用两个不同的配置文件

  • webpack.dev.js: 用于开发环境
  • webpack.prod.js: 用于生产环境

1. 两个配置文件

  • webpack.dev.js

    // 使用node的path模块
    const path = require('path')
    // 引入vue-loader插件
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    
    // 导入html-webpack-plugin
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    // 导入clean-webpack-plugin
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    
    // 导入webpack
    const webpack = require('webpack')
    
    module.exports = {
      // 模式
      mode: 'development',
      devtool: 'cheap-module-eval-source-map',
      // 打包的入口
      entry: './src/main.js',
      // devServer配置
      devServer: {
        // 指定服务器根目录
        contentBase: './dist',
        // 自动打开浏览器
        open: true,
        // 启用热模块替换
        hot: true
      },
      // 插件
      plugins: [
        // 请确保引入这个插件!
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
          template: './index.html'
        }),
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin()
      ],
    
      // 打包的出口
      output: {
        filename: 'app.js',
        path: path.resolve(__dirname, 'dist')
      },
      // 打包规则
      module: {
        rules: [
          {
            test: /\.vue$/,
            loader: 'vue-loader'
          },
          {
            test: /\.(jpg|jpeg|png|svg)$/,
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              limit: 2048
            }
          },
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          },
          {
            test: /\.styl(us)?$/,
            use: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader']
          }
        ]
      },
      resolve: {
        alias: {
          vue: 'vue/dist/vue.js'
        }
      }
    }
    
  • webpack.prod.js

    // 使用node的path模块
    const path = require('path')
    // 引入vue-loader插件
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    
    // 导入html-webpack-plugin
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    // 导入clean-webpack-plugin
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    
    module.exports = {
      // 模式
      mode: 'production',
      // 打包的入口
      entry: './src/main.js',
      // 插件
      plugins: [
        // 请确保引入这个插件!
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
          template: './index.html'
        }),
        new CleanWebpackPlugin()
      ],
    
      // 打包的出口
      output: {
        filename: 'app.js',
        path: path.resolve(__dirname, 'dist')
      },
      // 打包规则
      module: {
        rules: [
          {
            test: /\.vue$/,
            loader: 'vue-loader'
          },
          {
            test: /\.(jpg|jpeg|png|svg)$/,
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              limit: 2048
            }
          },
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          },
          {
            test: /\.styl(us)?$/,
            use: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader']
          }
        ]
      },
      resolve: {
        alias: {
          vue: 'vue/dist/vue.js'
        }
      }
    }
    

2. 提取公共部分

使用 webpack-merge 工具

1)安装

npm install -D webpack-merge

2). 创建config目录

创建 3 个文件

  • webpack.base.js: 公共配置
  • webpack.dev.js: 开发环境配置
  • webpack.prod.js: 生产环境配置
base
// 使用node的path模块
const path = require('path')
// 引入vue-loader插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')

// 导入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')

// 导入clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  // 打包的入口
  entry: './src/main.js',
  // 插件
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: './index.html'
    }),
    new CleanWebpackPlugin()
  ],

  // 打包的出口
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, '..', 'dist')
  },
  // 打包规则
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.(jpg|jpeg|png|svg)$/,
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          limit: 2048
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.styl(us)?$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'stylus-loader']
      }
    ]
  },
  resolve: {
    alias: {
      vue: 'vue/dist/vue.js'
    }
  }
}
dev
// 导入webpack
const webpack = require('webpack')

const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')

const devConfig = {
  // 模式
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  // devServer配置
  devServer: {
    // 指定服务器根目录
    contentBase: './dist',
    // 自动打开浏览器
    open: true,
    // 启用热模块替换
    hot: true
  },
  // 插件
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
}
module.exports = merge(baseConfig, devConfig)
prod
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
const prodConfig = {
  // 模式
  mode: 'production'
}
module.exports = merge(baseConfig, prodConfig)
3) package配置
"dev": "webpack-dev-server --config ./config/webpack.prod.js",
  "build": "webpack --config ./config/webpack.prod.js"

3. 测试

  • 警告

    entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.

  • 解决

     // webpack.base.js
    performance: {
        hints: false
      },
    

九 解析ES6语法

在项目中, 有时我们可能会使用 ES6 的语法, 而这些内容在低版本的浏览器上是不支持的. 往往不能正常解析.

一般, 我们会使用 babel 将 ES6 编译成 ES5 的语法.

  • 安装: npm install -D babel-loader @babel/core

  • 配置webpack

    module: {
      rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }]
    }
    
  • 安装env preset工具: npm install @babel/preset-env -D

  • 创建.babelrc配置文件

    { "presets": ["@babel/preset-env"] }
    

十 省略后缀和配置别名

resolve: {
  extensions: ['.js', '.vue'],
    alias: {
      vue: 'vue/dist/vue.js',
        '@': path.resolve(__dirname, '../src'),
          styles: path.resolve(__dirname, '../src/assets/styles')
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值