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

安装

  1. 安装webpack前要先安装Node.js
  2. 全局安装webpack:npm install webpack@3.6.0 -g(这里先指定3.6.0版本,因为vue cli2依赖该版本,可以进行手动配置,便于学习)
  3. 局部安装webpack:进入到项目根目录,输入npm install webpack@3.6.0 --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 babel-loader@7 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会找到idapp的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 uglifyjs-webpack-plugin@1.1.1 --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 webpack-dev-server@2.9.1
  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文件中的builddev脚本,添加--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')
  },
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值