天天看点

【学习笔记】webpack + Vue 组件化开发

文章目录

    • 安装
      • 为什么需要安装局部webpack
    • 使用
      • 准备工作
      • 基本使用
    • 配置
      • 入口和出口
      • webpack映射
    • loader
      • 打包css文件
      • 打包scss文件
      • 打包图片资源
        • file-loader
        • 修改文件名称
      • ES6 转 ES5
    • webpack 中的 Vue 使用
      • 编写Vue代码
        • 抽取template
        • vue-loader
      • 组件化开发Vue
        • 导入组件省略扩展名
    • webpack中的插件
      • loader和plugin区别
      • plugin的使用过程:
        • 添加版权插件
        • HTML打包插件
        • js压缩插件
    • 搭建本地服务器
    • 配置文件的抽离

安装

  1. 安装webpack前要先安装Node.js
  2. 全局安装webpack:

    npm install [email protected] -g

    (这里先指定3.6.0版本,因为vue cli2依赖该版本,可以进行手动配置,便于学习)
  3. 局部安装webpack:进入到项目根目录,输入

    npm install [email protected] --save-dev

为什么需要安装局部webpack

因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题

所以通常一个项目,都有自己局部的webpack

执行局部webpack打包:

node_modules/.bin/webpack

使用

准备工作

  1. 新建src文件,创建两个互相依赖的js文件
  2. 新建dist文件,保存打包后的文件
  3. 新建index.html文件(根目录下)

基本使用

在终端进入到项目根目录,输入命令:

webpack ./src/main.js ./dist/bundle.js

该命令的意思是将main.js在该项目内所依赖的所有js文件打包到dist目录下的bundle文件

如果报错禁止运行脚本,则需要配置powershell

配置

入口和出口

我们每次使用webpack的命令都需要写上入口和出口作为参数,非常麻烦,所以我们要创建一个webpack配置文件:webpack.config.js,在该文件配制好入口和出口后,下一次只需输入

webpack

即可打包

绝对路径需要依赖node的path包,一般只要依赖于node环境的项目,都需要创建package.json文件,该文件是告诉关于项目一些信息以及管理node包,使用

npm init

命令创建

如果项目有依赖一些node的包,使用

npm install

会根据package.json自动安装所需要的包

// 用于获取绝对路径,需要依赖node的path包,node全局中是包含该包的,所以不需单独建包
const path = require('path')
module.exports = {
  // 入口:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可
  entry: './src/main.js',
  // 出口:通常是一个对象,里面至少包含两个重要属性:path和filename
  output: {
    //__dirname:获取当前文件所在绝对路径
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
}
           

webpack映射

我们在真正的开发中一般使用

npm run build

来构建我们的项目,因为使用

webpack

命令时后面可能会跟很长的内容,我们想让这个长命令可以由其他命令代替

webpack映射还有一个好处,我们在任何终端执行webpack的时候都会执行全局webpack,但是我们使用映射的命令会优先执行局部webpack(本地),因为package.json中的scripts的脚本在执行时,会先寻找本地的node_modules/.bin路径中对应的命令,如果没找到,再去全局中寻找

打开package.json文件,在scripts对象内添加"build"属性

"scripts": {
  // 此时执行npm run build命令将转换为webpack命令
    "build": "webpack"
  },
           

loader

前面演示了如何打包js文件,但是如果我们想要打包css、vue、ts等文件时,需要先给webpack扩展对应的loader,这里以css为例演示

进入https://www.webpackjs.com/loaders/,找到对应的css的loader,按照文档操作

  1. 安装
  • css-loader:

    npm install --save-dev css-loader

  • style-loader:

    npm install style-loader --save-dev

  1. 进入webpack.config.js配置文件,在

    module.exports

    对象中添加

    module

    对象
module: {
    rules: [
      {
        test: /\.css$/,
        // css-loader:只负责加载,但不负责解析,也不负责与html文件连接
        // style-loader:将模块的导出作为样式添加到 DOM 中
        // webpack在使用多个loader时,是从右向左读取,我们要先加载css,在添加样式,所以要将css-loader放到右边
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
           

打包css文件

安装并配置完css的loader,我们就可以开始打包了

我们使用webpack打包文件时,只会打包入口js文件所依赖的文件,不被它依赖的文件不会被打包;所以我们将css等文件添加为入口js的依赖就能带着css文件一起打包了

// 依赖css文件
require('./css/normal.css')
           

打包scss文件

如果我们希望在项目中使用scss、less、stylu来写样式,webpack是否可以帮助我们处理呢?我们这里以scss为例,其他也是一样的

注意:我们使用Live Sass Compiler插件会实时将scss文件转换为css文件,这些css文件可以作为我们开发时调整样式使用,在webpack打包时只打包scss文件即可

步骤:

  1. 创建一个scss文件,然后放在css文件夹中
  2. 在入口js文件依赖该scss文件:

    require('./css/special.scss')

  3. 执行命令:

    npm install sass-loader node-sass webpack --save-dev

  4. 进入webpack.config.js文件,我们在配置css的loader的时候已经添加了module对象,因此直接在module对象的rules数组里添加一个新的对象元素
  5. 执行

    npm run build

    ,如果报错getResolve is not a function,则是因为scss安装的版本过高,需卸载重装其他版本
// 在webpack.config.js中配置loader
{
   test: /\.scss$/,
   use: [{
       loader: "style-loader" // 将 JS 字符串生成为 style 节点
   }, {
       loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
   }, {
       loader: "sass-loader" // 将 Sass 编译成 CSS
   }]
   }
           

打包图片资源

我们在images文件夹加入两张图片:一张小于8kb的图片和一张大于8kb的图片,待会儿我们会针对这两张不同大小的图片进行不同的处理

我们已经将normal.css文件作为main.js的依赖了,所以我们在该css文件中设置背景图片也会作为main.js的依赖;但我们需要为图片单独添加loader

步骤:

  1. 在项目根目录执行命令:

    npm install --save-dev url-loader

  2. 进入webpack.config.js文件,在已经创建好的module对象里的rules数组添加一个新的对象元素
{
  // 设置匹配的图片格式
  test: /\.(png|jpg|gif)$/,
  use: [
    {
      loader: 'url-loader',
      options: {
        // 限制图片大小,图片超出limit会用file-loader来打包,需安装file-loader
        // 图片小于limit时,会将图片url地址编译成base64格式字符串
        limit: 8192
      }
    }
  ]
}
           

file-loader

当我们使用的图片超出了url-loader配置的大小范围,则不会再以url-loader来进行加载,而是以file-loader来加载

如果没有安装file-loader则会报错

  1. 安装file-loader:

    npm install --save-dev file-loader

    ,无需对file-loader专门进行配置

当我们使用file-loader加载图片后,发现在dist文件夹下生成了一个以hash值命名的图片,也就是我们的源图片;但在网页中该图片并没有显示,因为webpack打包后的文件url路径默认是根目录,而不是dist目录,因此需要修改默认url路径

  1. 进入webpack.config.js文件,在output对象中添加

    publicPath

    属性,以后任何涉及到url的路径,都会在前面拼接一个

    dist/

output: {
    publicPath: 'dist/'
  },
           

注意:此时我们的index.html在项目根目录,所以访问资源的时候需要添加

dist/

路径;到后期我们也会将index.html打包到dist目录下,到时候需要将

publicPath

属性删掉

修改文件名称

使用file-loader打包文件时会在dist文件夹生成一个32位hash值命名的文件,目的是防止文件名重复

但是在真实开发中我们希望将所有的图片放在dist下的images文件夹下,附带图片原来的名称,同时也要防止重复

因此我们需要在webpack.config.js文件中进行配置:

{
   // 1. 找到url-loader的配置项
   test: /\.(png|jpg|gif)$/,
   use: [{
     loader: 'url-loader',
     options: {
       limit: 10000,
       // 2. 在options中添加name属性
       name: 'images/[name].[hash:8].[ext]'
     }
   }]
}
           

images/[name].[hash:8].[ext]

:表示生成的文件会在dist的images文件夹下并以原来的名字+8位哈希值+原后缀命名,如:

欧阳娜娜.d96873f5.jpg

ES6 转 ES5

webpack在打包我们的js文件的时候并没有完全的将ES6语法转换为ES5,因此我们需要借助babel来帮我们转换

步骤:

  1. 安装babel:

    npm install --save-dev [email protected] babel-core babel-preset-es2015

  2. 进入webpack.config.js文件,在已经创建好的module对象里的rules数组添加一个新的对象元素来配置babel-loader
{
   test: /\.js$/,
   // 排除掉node_modules文件夹
   exclude: /(node_modules|bower_components)/,
   use: {
     loader: 'babel-loader',
     options: {
       presets: ['es2015']
     }
   }
}
           

webpack 中的 Vue 使用

  1. 安装Vue:

    npm install vue --save

  2. 在main.js文件中导入Vue:

    import Vue form 'vue'

  • 这里直接写

    from vue

    即可,如果不加相对路径,会默认去node_modules文件夹中导入,而我们的Vue就安装在此文件夹
  • 并且node_modules会执行

    export default Vue

    ,所以我们直接

    import Vue

    即可
  1. 此时我们编写一段Vue代码,打包并运行,发现报错using the runtime-only,我们Vue在构件时默认构建runtime-only版本,该版本不允许出现

    <template>

    ,而我们的

    <div id="app">

    其实就是Vue实例的template,所以会报错
  2. 修改vue构建版本:进入webpack.config.js文件,在module.exports对象中添加一条属性:
resolve: {
    alias: {
      // 下次import Vue from 'vue'的时候会来这里看'vue'是否指向了具体的文件,这里指向了vue.esm.js
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
           
  1. 重新打包

编写Vue代码

假设我们实例化了一个Vue对象:

const app = new Vue({
  el:'#app',
  data:{
    msg:'我是一条msg'
  }
})
           

我们以前想要输出msg的时候会这样写:

<div id="app">
  {{msg}}
</div>
           

在真实开发中我们的

<div id="app"> </div>

里面不会写任何内容,我们会写在Vue对象的

template

属性中,用反单引号包裹:

new Vue({
  el:'#app',
  template:`
  <div>
    <button @click="click">我是template</button>
    <h2>{{message}}</h2>
  </div>
  `,
  data:{
    message:'我是一条message'
  },
  methods:{
    click() {
      console.log('我被点击了')
    }
  }
})
           

这样在编译程序的时候,

template

会找到id为app的DOM元素,并将其覆盖替换为

template

中的代码

抽取template

前面我们将

<template>

代码和它所需要的

message

数据和

click()

方法都写到了Vue对象中,显得非常冗余,因此我们要将它们抽取成一个单独的组件

// 创建APP组件:
const APP = {
  template:`
  <div>
    <button @click="click">我是template</button>
    <h2>{{message}}</h2>
  </div>
  `,
  data() {
    return {
      message:'我是一条message'
    }
  },
  methods: {
    click() {
      console.log('我被点击了')
    }
  }
} 

// 现在的Vue对象:
new Vue({
  el:'#app',
  template:``,
  data:{
  },
  methods:{
  }
})
           

我们想要使用该组件,则需要在Vue对象中注册它,然后直接在

template

属性中使用它即可,因为

template

中的内容会覆盖替换掉id为app的DOM元素

new Vue({
  el:'#app',
  // 将APP组件中的内容覆盖替换id为app的DOM元素,注意组件的单标签形式 斜杠在右边
  template:'<APP/>',
  // 注册APP组件
  components:{
    APP
  }
})
           

虽然我们将该组件抽取了出来,但是main.js作为程序入口,不应该保存这些组件代码,而且当组件过多时代码显得更加混乱,因此我们要为该组件创建一个单独的js文件单独保存

在src目录下创建一个vue文件夹,再创建一个APP.js文件,在该js文件里面专门书写该APP组件,并导出该组件供其他文件引用:

export default {
  template: `
  <div>
  <button>我是template</button>
  <h2>{{message}}</h2>
  </div>
  `,
  data() {
    return {
      message: '给我一个吻'
    }
  }
}
           

导出了该组件,我们就可以在main.js文件中导入使用了:

import APP from './vue/APP'

// 直接在Vue对象中使用APP组件
new Vue({
  el: '#app',
  // APP组件的内容会替换到id为app的DOM元素上
  template: '<APP/>',
  components: {
    APP
  }
})
           

虽然我们用APP.js单独保存该APP组件,但是该组件的模板和js代码没有进行分离,所以用js文件保存组件是不合适的,要使用 .vue文件

我们要在vue文件夹下创建APP.vue文件,vue格式文件包含了三部分:

  • <template>

    :书写组件标签
  • <script>

    :书写js代码
  • <style>

    :书写样式

我们将以前的APP.js文件中的代码按照分类放入vue格式的文件中:

<template>
  <div>
    <button>我是template</button>
    <h2>{{message}}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "给我一个吻"
    };
  },
};
</script>

<style>
</style>
           

是不是感觉条理清晰了很多,而且我们可以为

<template>

内的标签添加样式,写在下面的

<style>

标签中即可

删除以前导入的js文件,修改为:

import APP from './vue/APP.vue'

vue-loader

我们webpack加载css、scss都需要下载对应的loader,加载vue也不例外:

  1. 安装:

    npm install vue-loader vue-template-compiler --save-dev

  2. 配置:进入webpack.config.js文件,在module对象的rules数组里添加一个新的对象元素:
{
  test: /\.vue$/,
  use: ['vue-loader']
}
           

此时我们执行webpack打包会报错,因为我们的vue-loader版本过高,我们可以配置为较低的版本来解决此问题

进入package.json,将

vue-loader

的值改为

^13.0.0

,这个

^

的意思是会将版本修改为大于13.0的版本但小于14.0版本中的一个,然后执行

npm install

会自动安装该版本

  1. 重新打包

组件化开发Vue

下面我们来感受一下什么是真正的组件化开发

  1. 我们在vue文件夹下再新建一个Cpn.vue,在

    <template>

    标签内书写一些内容,最后的代码如下:
<template>
  <div>
    <h2>我是Cpn里的文字</h2>
    <h2>我是Cpn里的文字</h2>
    <h2>我是Cpn里的文字</h2>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>
           
  1. 打开之前的APP.vue文件,在

    <script>

    标签中导入刚才创建的Cpn组件:

    import Cpn from './Cpn.vue'

  2. 注册刚导入的Cpn组件
  3. 在APP的

    <template>

    中使用Cpn组件

此时的APP.vue整体代码如下:

<template>
  <div>
    <button>我是template</button>
    <h2 class="title">{{message}}</h2>
    <!-- 使用导入的Cpn组件 -->
    <Cpn />
    <Cpn />
  </div>
</template>

<script>
// 导入Cpn组件
import Cpn from "./Cpn.vue";
export default {
  data() {
    return {
      message: "给我一个吻",
    };
  },
  // 注册一下刚导入的Cpn组件
  components: {
    Cpn,
  },
};
</script>

<style scoped>
.title {
  color: green;
}
</style>
           

以后我们的开发模式都是这样写组件的,到时候我们的应用程序就是一个组件树

以现在的代码为例,main.js引入了APP组件,APP组件就是根组件,而根组件又引入了Cpn组件,Cpn组件以后又引入了其他组件,每个组件都是一个独立的文件

导入组件省略扩展名

进入webpack.config.js文件,在resolve对象中添加:

// 导入js和vue后缀的文件时不需在写扩展名,这两个必须同时添加
  extensions: ['.vue', '.js']
           

webpack中的插件

webpack中的插件(plugin),就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等

loader和plugin区别

  • loader主要用于转换某些类型的模块,它是一个转换器
  • plugin是插件,它是对webpack本身的扩展,是一个扩展器

plugin的使用过程:

  1. 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
  2. 在webpack.config.js中的plugins中配置插件

添加版权插件

我们先来使用一个最简单的插件,为打包的文件添加版权声明

该插件名字叫BannerPlugin,属于webpack自带的插件

  1. 进入webpack.config.js,由于该插件集成到了webpack包中,所以我们要导入webpack包
  2. 在module.exports对象中添加plugins数组
  3. 配置BannerPlugin
const webpack = require('webpack')

module.exports = {
  ...
  plugins: [
    new webpack.BannerPlugin('最终版权归我所有~')
  ]
}
           

HTML打包插件

目前,我们的index.html文件是存放在项目的根目录下的

在真实发布项目时,发布的是dist文件夹中的内容,所以我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件

该插件的作用:

  • 自动生成一个index.html文件(可以指定模板来生成)
  • 将打包的js文件,自动通过script标签插入到body中

使用步骤:

  1. 安装插件:

    npm install html-webpack-plugin --save-dev

    ,最高版本会报错,建议安装3.0版本
  2. 引入插件:在webpack.config.js中输入:

    const HtmlWenpackPlugin = require('html-webpack-plugin')

  3. 在plugins数组中添加:

    new HtmlWebpackPlugin()

现在重新打包就能在dist文件夹生成我们的index.html文件了,但是还存在两个问题:

  1. 我们之前学习的时候给file-loader的src路径添加了

    dist/

    ,现在我们的html文件已经被打包到该路径了,所以不再需要
  2. 我们希望自动生成index.html的时候添加一个

    <div id="app"> </div>

    标签

解决:

  1. 进入webpack.config.js,将

    output

    对象中的

    publicPath: 'dist/'

    属性删掉
  2. 将根目录下的index.html文件作为打包html文件时的模板:进入webpack.config.js文件,在plugins数组中的配置中添加:
new HtmlWebpackPlugin({
      // 以当前配置文件所在目录下的index.html为模板
      template: 'index.html'
    })
           

js压缩插件

  1. 安装:

    npm install [email protected] --save-dev

  2. 修改webpack.config.js文件:
// 引入插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  ...
  plugins:[
    new UglifyJsPlugin()
  ]
}
           

搭建本地服务器

之前我们每次调试一点代码都要重新打包,很不方便

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果

  1. 安装

    npm install --save-dev [email protected]

  2. 在webpack.config.js文件中配置devServer

devServer也是作为webpack中的一个选项,选项本身可以配置如下属性:

  • contentBase

    :为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
  • port

    :端口号
  • inline

    :页面实时刷新
  • historyApiFallback

    :在SPA页面中,依赖HTML5的history模式
// 在webpack.config.js文件中配置
module.exports = {
  ...
  devServer: {
    contentBase: './dist',
    inline: true
  }
}
           

如果直接在控制台输入

webpack-dev-server

来运行的话会执行全局webpack,因此我们可以在package.json的scripts对象中添加脚本:

"dev": "webpack-dev-server --open"

,(添加–open,在开启服务时会自动打开浏览器)

这样我们只需要在终端输入

npm run dev

,就可以在本地webpack运行我们的服务了,我们修改src目录下的文件代码时,浏览器就会自动刷新

想要终止服务,在终端按下

ctrl+c

选择退出

总结:我们通过

webpack-dev-server

搭建了一个本地服务,之后我们的测试都是在本地服务中进行测试,等全部测试完毕之后,就可以执行

npm run build

进行真正的打包,再把打包后的dist文件夹放到服务器进行部署

配置文件的抽离

在webpack.config.js中,有一些配置是在开发环境中不需要的,如js压缩插件;有一些配置是生产环境不需要的,如devserver

我们希望将配置文件分为三份,分别为公共配置、开发配置、生产配置。在程序开发时,执行公共配置和开发配置;在程序上线后,执行公共配置和生产配置,因此还需要文件合并工具

步骤:

  1. 安装文件合并工具:

    npm install webpack-merge --save-dev

    ,推荐安装4.0版本,高版本会报错,参考https://blog.csdn.net/sinat_37503265/article/details/107216055
  2. 在根目录新建build文件夹用来保存配置文件
  3. 新建base.config.js,dev.config.js,prod.config.js三个配置文件
  4. 将我们以前的webpack.config.js中的配置项按照分类分别放到这三个文件中
  5. 合并文件,这里以合并公共配置和生产配置为例,打开生产环境配置文件:
// 导入js压缩插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 导入webpack-merge插件
const webpackMerge = require('webpack-merge')
// 导入base.config.js
const baseConfig = require('./base.config')
// exports导出的时候,将base.config和prod.config一起导出
module.exports = webpackMerge(baseConfig, {
  // 原来prod.config中的代码
  plugins: [
    new UglifyJsPlugin()
  ],
})
           
  1. 修改默认配置文件,修改package.json文件中的

    build

    dev

    脚本,添加

    --config

    字段
{
  ...
  "scripts": {
    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
  },
}
           
  1. 修改webpack打包目录,进入base.config.js,修改path:
module.exports = {
  output: {
    path: path.resolve(__dirname, '../dist')
  },
}
           

继续阅读