0%

Vue学习笔记(三)-前端模块化

3 前端模块化

3.1 ES模块化的导入和导出

export基本使用

export指令用于导出变量,比如下面的代码:

//info.js
export let name = 'freedom'
export let age = 18
export let height = 1.88

上面的代码还有另一种写法:

// info.js
let name = 'freedom'
let age = 18
let height = 1.88

export {name, age, height}

在需要使用的地方导入:

import {name, age, height} from  './info.js'

export default

某些情况下,一个模块中包含某个功能,我们并不希望给这个功能命名,而且让导入者自己来命名

  • 这个时候就可以使用export default

    // info.js
    export default function() {
    console.log('default function');
    }
  • 我们来到main.js中,这样使用就可以了

    • 这里myFunc是我自己命名的,你可以根据需要命名它对应的名字
import myFunc from './info.js'

myFunc()
  • 另外要注意:

    • export default在同一个模块中,不允许同时存在多个。

    • 但是通常情况下我们需要给*起一个别名,方便后续使用

      import * as info from './info.js'

      console.log(info.name, info.age, info.height);

3.2 WebPack

3.2.1 前模块化

  • 在前面学习中,我已经用了大量的篇幅解释了为什么前端需更模块化。而且也提到了目前使用前端模块化的一些方案: AMD. CMD. CommonJs, ES6.
  • 在ES6之前,我们要想进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发,并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。
  • 而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系,而且不仅仅是JavaScript文件,我们的CSS,图片、json文件等等在webpack中都可以被当做模块来使用(在后续我们会看到)。

这就是webpack中模块化的概念

打包如何理解呢

  • 理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了.
  • 就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle).
  • 并且在打包的过程中,还可以对资源进行处理比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。
  • 但是打包的操作似乎grunt/gulpt可以帮助我们完成,它们有什么不高呢

WebPack模块打包

WebPack为了可以正常运行,必须依赖node环境,node环境为了可以正常执行很多代码,必须包含各种各样依赖的包,npm工具是node的包管理工具。

3.2.2 WebPack安装

安装WebPack首先需要安装Node.js,Node.js自带了软件包管理工具npm

查看自己的node版本:node -v

全局安装WebPack(这里我们先指定3.6版本)

npm install webpack@3.6.9 -g

局部安装WebPack(后续才需要)

cd 对应的目录
npm install webpack@3.6.0 --save-dev

–save-dev 是开发时依赖,项目打包后不需要继续使用的

为什么全局安装后,还需要局部安装呢?

  • 在终端直接执行WebPack命令,使用的全局安装的WebPack
  • 当在package.json中定义了scripts时,其中包含了WebPack命令,那么使用的是局部WebPack

3.2.3 WebPack简单使用

目录结构如下:

image-20200206145331311

文件内容如下:

info.js:

export const name = 'freedom';
export const age = 18;
export const height = 1.80;

mathUtils.js:

function add(num1, num2) {
return num1 + num2
}

function mul(num1, num2) {
return num1 * num2
}

module.exports = {
add,
mul
}

在main.js中使用上述两个文件中内容:

const {add, mul} = require('./mathUtils.js')

console.log(add(20, 30));
console.log(mul(20, 30));

import {name, age, height} from './info.js'

console.log(name);
console.log(age);
console.log(height);

此时在命令行使用webpack ./src/main.js ./dist/bundle.js命令,将上述文件打包到dist目录下的bundle.js文件中

D:\vue\vue_learning\webpack>webpack ./src/main.js ./dist/bundle.js
Hash: 259c6b56d630dacf141e
Version: webpack 3.6.0
Time: 60ms
Asset Size Chunks Chunk Names
bundle.js 3.58 kB 0 [emitted] main

如上所示,表明打包成功。

在index.html中引入bundle.js:

<body>
。。。。。。
<script src="./dist/bundle.js"></script>
</body>

可以在页面控制台看到之前main.js中引入的代码已经正确执行:

image-20200206150133931

JS文件打包

现在的js文件中使用了模块化的方式进行开发,他们可以直接使用吗?不可以。

  • 因为如果直接在index.htmlsl入这两个js文件,浏览器并不识别其中的模块化代码。
  • 另外,在真实项目中当有许多这样的js文件时,我们一个个引用非常麻烦,并且后期非常不方便对它们进行管理。

我们应该怎么做呢?使用webpack工具,对多个js文件进行打包。

  • 我们知道, webpack就是一个模块化的打包工具,所以它支持我们代码中写模块化,可以对模块化的代码进行处理。(如何处理的,待会儿在原理中,我会讲解)
  • 另外,如果在处理完所有模块之间的关系后,将多个js打包到一个js文件中,引入时就变得非常方便了。OK,如何打包呢?使用webpack的指令即可

局部安装WebPack

目前,我们使用的webpack是全局的webpack,如果我们想使用局部来打包呢?

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

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

第一步, 项目中需要安装自己局部的webpack

  • 这里我们让局部安装webpack3.6.0

  • Vue CL13中已经升级到webpack4 ,但是它将配置文件隐藏了起来,所以查看起来不是很方便

    npm install webpack@3.6.0 --save-dev

第二步,通过node_moduels/.bin/webpack启动WebPack打包

D:\vue\vue_learning\webpack>node_modules\.bin\webpack
Hash: 259c6b56d630dacf141e
Version: webpack 3.6.0
Time: 54ms
Asset Size Chunks Chunk Names
bundle.js 3.58 kB 0 [emitted] main
[0] ./src/main.js 212 bytes {0} [built]
[1] ./src/mathUtils.js 156 bytes {0} [built]
[2] ./src/info.js 85 bytes {0} [built]

package.json中定义启动

但是,每次执行都敲这么一长串有没有觉得不方便呢?

  • ок,我们可以在package.json的scripts中定义自己的执行脚本。

package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。

  • 首先,会寻找本地的node_modules/.bin路径中对应的命令。
  • 如果没有找到,会去全局的环境变量中寻找。
  • 如何执行我们的build指令呢?npm run build
{
"name": "meet_webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack" # 这里,npm会在scripts找build对应的命令
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^3.6.0"
}
}

3.2.4 loader

什么是loader

loader是webpack中一个非常核心的概念。

  • webpack用来做什么呢?
    • 在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。
    • 但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss, less转成css ,将jsx. .vue文件转成js文件等等
    • 对于webpack本身的能力来说,对于这些转化是不支持的

那怎么办呢?给webpack扩展对应的loader就可以啦。

loader使用过程

  • 步骤一:通过npm安装需要使用的loader

  • 步骤二:在webpack.config.js中的modules关键字下进行配置

大部分loader我们都可以在webpack的官网中找到,并且学习对应的用法

css文件处理— style-loader

我们来安装style-loader:

npm instatt -save-dev style-loader

注意: style-loader需要放在css-loader的前面。

  • 疑惑:不对吧?按照我们的逻辑,在处理css文件过程中,应该是css-loader先加载css文件,再由style-oader来进行进一步的处理,为什么会将style-loader放在前面呢?
  • 答案:这次因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。

目前, webpack.config.js的全部配置如下:

const path = require('path')

module.exports = {
// 入口: 可以是字符串/数组/对象,这里我们的入口只有一个,
// 所以写一个字符串即可
entry: './src/main.js',
// 出口:通常是一个对象,里面至少要包含两个重要属性, path 和filename
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
// css-loader只负责将css文件进行加载
// style-loader负责将样式加载到DOM中
// 使用多个loader时,是从右向左读的
use: ['style-loader', 'css-loader']
}
]
}
}

less文件处理

继续在官方中查找,我们会找到ess-loader相关的使用说明.和css文件类似:

  • 首先,还是需要安装对应的loader

    • 注意:我们这里还安装了less ,因为webpack会使用less对less文件进行编译

      npe install -save-dev less-loader tess

  • 其次,修改对应的配置文件

    • 添加一个rules选项,用于处理less文件
module: {
rules: [
......
{
test: /\.less$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},
{
loader: "css-loader" // translates CSS into CommonJS
},
{
loader: "less-loader" // compiles Less to CSS
}
]
}
]
}

图片文件处理

和上面类似,使用npm命令安装相关包:

npm install --save-dev url-loader //对于小于指定大小的会由url-loader编译为base64字符串,后有说明
npm install --save-dev file-loader //对于大于指定大小的会由file-loader作为文件引入

只有在webpack.config.js中新增配置:

output: {
。。。。。。
publicPath: 'dist/' # 指定文件相对路径的起始目录
},
module: {
rules: [
。。。。。。
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片小于limit时,会将图片编译成base64字符串形式
// 当加载的图片大于limit时,需要使用file-loader模块进行处理
limit: 8192
}
}
]
}
]
}
  • 我们发现webpack自动帮助我们生成一个非常长的名字

    • 这是一个32位hash值,目的是防止名字重复

    • 但是,真实开发中,我们可能对打包的图片名字有一定的要求

    • 比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时也防止重复

      最终正确配置:

      {
      test: /\.(png|jpg|gif)$/,
      use: [
      {
      loader: 'url-loader',
      options: {
      // 当加载的图片小于limit时,会将图片编译成base64字符串形式
      // 当加载的图片大于limit时,需要使用file-loader模块进行处理
      limit: 8192,
      name: 'img/[name].[hash:8].[ext]' //定义命名规则
      }
      }
      ]
      }
  • 所以,我们可以在options中添加上如下选项:

    • img :文件要打包到的文件夹
    • name :获取图片原来的名字,放在该位置
    • hash:8:为了防止图片名称中突,依然使用hash ,但是我们只保留8位Dext:使用图片原来的扩展名
  • 但是,我们发现图片并没有显示出来,这是因为图片使用的路径不正确

    • 默认情况下, webpack会将生成的路径直接返回给使用者
    • 但是,我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/

ES6转ES5

如果你仔细阅读webpack打包的js文件,发现写的ES6语法并没有转成ES5 ,那么就意味着可能一些对ES6还不支持的浏览器没有办法很好的运行我们的代码

  • 在前面我们说过,如果希望将ES6的语法转成ES5 ,那么就需要使用babel.

    • 而在webpack中,我们直接使用babel对应的loader就可以了

      npm install babel-loader babel-core babel-preset-env webpack

  • 之后同样修改webpack.config.js文件

    {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
    loader: 'babel-loader',
    options: {
    presets: ['es2015']
    }
    }
    }

之后重新编译即可

3.3 Vue Cli

3.3.1 介绍

如果你只是简单写几个Vue的demo程序,那么你不需要Vue Cli

如果你在开发大型项目,那么你需要并且必然需要使用Vue Cli

  • 使用Vue.js开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情
  • 如果每个项目都要手动完成这些工作,那无疑效率比较低,所以通常我们会使用一些脚手架工具来帮助完成这些事情

cli是什么意思?

  • cli是Command-Line-Interface,翻译为命令行界面,但是俗称脚手架
  • Vue Cli是一个官方发布vue.js项目脚手架
  • 使用vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置

Vue-cli使用前提

  1. 安装NodeJS
  2. 检测安装的版本
    • 默认情况下自动安装Node和NPM
    • Node环境要求8.9以上或者更高版本node -v

notes: 由于国内直接使用npm官方镜像是非常慢的,这里推荐使用淘宝NPM镜像

你可以使用淘宝定制的cnpm(gzip压缩支持)命令行工具代替默认的npm:

npm install -g cnpm --registry=https://registry.npm.taobao.org

这样就可以使用cnpm命令来安装模块了:cnpm install [name]

  1. WebPack

    • Vue.js官方脚手架工具就使用了webpack模板

      • 对所有的资源会压缩等优化操作
      • 它在开发过程中提供了一套完整的功能,能够使得我们开发过程中变得高效
    • webpack全局安装:npm install webpack -gs

什么是NPM呢?

  • NPM的全称是Node Package Manager
  • 是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准
  • 后续我们会经常使用NPM来安装一些开发过程中依赖包

3.3.2 Vue Cli的使用

安装Vue脚手架

npm install -g @vue/cli

注意: 上面安装的是Vue Cli 3的版本,如果需要安装Vue Cli 2的方式初始化项目是不可以的

notes: 注意3版本是不能直接兼容2版本的,需要使用如下命令拉取2版本的模板:npm install @vue/cli-init -g

vue cli 2初始化项目: vue init wepack [my-project]

vue cli 3初始化项目: vue create [my-project]

vue-cli3 与 2的区别

  • vue-cli3 是基于webpack 4打造,vue-cli2 还是基于webpack 3
  • vue-cli3的设计原则是“0配置”, 移除了配置文件的.buildconfig等目录
  • vue-cli3提供了vue ui命令,提供了可视化配置,更加人性化
  • 移除了static文件夹,新增了public文件夹,并且index.html移动到public中

Runtime-Compiler和Runtime-only的区别

  • 如果你需要在客户端编译模板(例如,向template选项传入一个子模板中的非dom和HTML挂载到一个元素),你需要带有编译器的版本

    // 这种情况需要编译器(compiler)
    new Vue({
    template: '<div>{{ hi }}</div>'
    })

    // 这种情况不需要编译
    new Vue({
    render (h) {
    return h('div', this.hi)
    }
    })

    在使用vue-loader或vueify时, *.vue文件中的模板会在构建时(build time)预编译(precompilet为Javascript,最终生成的bundle中你不再需要编译器(compilen),因此可以直接使用只含有进行时的构建版本runtime ontyl.

    由于只含有运行时构建版本runtime-only)t完整构建版本full-bulil轻大约30%,你应该尽可能使用只含有运行时的构建版本,如果你还是希望使用完整构建版本,则常要在打包器中配置名

    由于运行时版本的构建比其全面版本的重量轻约30%,因此你可以随时使用它,如果你仍然希望使用完整版本,则需要在期M程序中配置别名:

vue 3目录结构详解

public/ 相当于CLI2中的static目录
src/ 源代码目录
.browserslistrc 浏览器相关支持情况
.gitignore git忽略的文件
babel.config.js ES语法转换
postcss.config.js CSS相关转换

3.3.3 补:匿名函数

ES5函数写法:

const obj = function aaa() {
return 100
}

匿名函数的写法:

const obj = () => {
return 100
}

放入一个参数

const power = num => {
return num * num
}

放入两个参数

const sum = (num1, num2) => {
return num1 + num2
}

函数中

// 函数代码块中有多行代码时
const test = () => {
// 1.打印hello world
console.log("Hello World");
// 2.打印Hello Vuejs
console.log('Hello Vuejs');
}
// 函数代码块中只有一行代码
const sum = (num1, num2) => {
return num1 * num2
}
// 可以简写为
const mul = (num1, num2) => num1 * num2
// 只有一行打印时
const demo = () => {
console.log('Hello Demo');
}
// 可以简写为
const demo = () => console.log('Hello Demo')

箭头函数的this使用

// 什么时候使用箭头
setTimeout(function () {
console.log(this);
}, 1000)

setTimeout(() => {
console.log(this);
}, 1000)
// 问题: 箭头函数中的this是如何查找的
// 结论:向外层作用域中,一层层查找this, 直到有this的定义
const obj = {
aaa() {
setTimeout(function() {
console.log(this); // window
})
}

setTimeout(() => {
console.log(this); // obj对象
})
}

欢迎关注我的其它发布渠道