其他章节请看:

vue 快速入门 系列

vue loader 扩展

vue loader一文中,我们学会了从零搭建一个简单的,用于单文件组件开发的脚手架。本篇将在此基础上继续引入一些常用的库:vue-router、vuex、axios、mockjs、i18n、jquery、lodash。

环境准备

Tip: 此环境本质就是“vue loader”一文最终生成的代码,略微精简一下:删除不必要的文件、wepback.config.js 注释掉 eslint 以及自定义 loader。

项目结构:

vue-loader-test
- src // 项目源码
- index.html // 页面模板
- index.js // 入口
- package.json // 存放了项目依赖的包
- webpack.config.js // webpack配置文件
// index.html

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

console.log('hello');
// package.json

{
"name": "vue-loader-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/preset-env": "^7.14.7",
"babel-loader": "^8.2.2",
"css-loader": "^5.2.4",
"eslint": "^7.30.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-vue": "^7.12.1",
"eslint-webpack-plugin": "^2.5.4",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.2",
"less": "^4.1.1",
"less-loader": "^7.3.0",
"mini-css-extract-plugin": "^1.6.2",
"node-sass": "^6.0.1",
"postcss-loader": "^4.3.0",
"postcss-preset-env": "^6.7.0",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"sass-loader": "^10.2.0",
"style-loader": "^2.0.0",
"stylus": "^0.54.8",
"stylus-loader": "^4.3.3",
"ts-loader": "^7.0.5",
"typescript": "^4.3.5",
"url-loader": "^4.1.1",
"vue": "^2.6.14",
"vue-loader": "^15.9.7",
"vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
}
}
// webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {
VueLoaderPlugin
} = require('vue-loader'); const process = require('process');
process.env.NODE_ENV = 'production'
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const postcssLoader = {
loader: 'postcss-loader',
options: {
// postcss 只是个平台,具体功能需要使用插件
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
browsers: 'ie >= 8, chrome > 10',
},
],
]
}
}
}
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
oneOf: [
// 这里匹配 `<style module>`
{
resourceQuery: /module/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
// 开启 CSS Modules
modules: {
// 自定义生成的类名
localIdentName: '[local]_[hash:base64:8]'
} }
}
]
},
{
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader'
]
},
]
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
hotReload: true // 关闭热重载
}
},
{
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
// 调整的比 6.68 要小,这样图片就不会打包成 base64
limit: 1024 * 6,
esModule: false,
},
},],
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.sass$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
// sass-loader version >= 8
sassOptions: {
indentedSyntax: true
},
additionalData: `$size: 3em;`,
}
}
]
},
{
test: /\.less$/,
use: [
'vue-style-loader',
// +
{
loader: 'css-loader',
options: {
// 开启 CSS Modules
modules: {
localIdentName: '[local]_[hash:base64:8]'
} }
},
postcssLoader,
'less-loader'
]
},
{
test: /\.styl(us)?$/,
use: [
'vue-style-loader',
'css-loader',
'stylus-loader'
]
},
{
test: /\.js$/,
// exclude: /node_modules/,
exclude: file => (
/node_modules/.test(file) &&
!/\.vue\.js/.test(file)
),
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: { appendTsSuffixTo: [/\.vue$/] }
},
{
test: /\.pug$/,
loader: 'pug-plain-loader'
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new VueLoaderPlugin(),
new MiniCssExtractPlugin(),
],
mode: 'development',
devServer: {
hot: true,
open: true,
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
},
extensions: ['.ts', '.js'],
},
};

安装依赖,启动服务,如果在自动打开的网页的控制台中输出:hello,则说明环境准备就绪。

> npm i
// 启动服务
> npm run dev

vue-router

Tip:由于 vue-router 官网的"起步"章节,不是以单页面组件的形式介绍的,所以笔者另外参考了 vue-cli 创建带有 vue-router 的项目。

将 vue-router 引入工程,步骤如下:

> npm i vue-router@3

创建 App.vue,包含一个导航,导航指向两个组件,分别是 Home 和 About:

// src/App.vue

<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</div>
</template>

在 src/views 文件夹中创建两个组件:

// views/Home.vue

<template>
<div>
i am Home
</div>
</template>
// views/About.vue

<template>
<div>
i am About
</div>
</template>

创建路由,并将路由统一放在 src/router 文件夹中:

// router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入 Home 组件
import Home from '../views/Home.vue' // 在一个模块化工程中使用 vue-router,必须通过 Vue.use() 明确地安装路由功能
Vue.use(VueRouter) // 定义路由 map
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
] // 创建路由实例
const router = new VueRouter({
routes
}) export default router

最后在 src/index.js 中使用路由:

// index.js

import Vue from 'vue';
import router from './router';
import App from './App.vue'; new Vue({
// 通过 router 配置参数注入路由,从而让整个应用都有路由功能
router,
el: 'body',
render: (h) => h(App),
});

重启服务,页面显示如下:

Home | About
i am home

默认是导航是 Home,点击 About,主体内容则变成i am About

vuex

Tip: 参考 vue-cli 创建的带有 vuex 的项目。

将 vuex 引入工程,步骤如下:

> npm i vuex@3

创建 vuex 文件,统一将 vuex 放在 src/store 文件夹中:

// store/index.js

import Vue from 'vue';
import Vuex from 'vuex'; // 在一个模块化的打包系统中,必须显式地通过 Vue.use() 来安装 Vuex
Vue.use(Vuex); export default new Vuex.Store({
state: {
count: 0,
},
mutations: {
},
actions: {
},
modules: {
},
});

通过 store 配置参数注入 vuex,从而让整个应用都有 vuex 功能:

// src/index.js

+ import store from './store';

new Vue({
// 为了在 Vue 组件中访问 this.$store property,需要为 Vue 实例提供创建好的 store
// Vuex 提供了一个从根组件向所有子组件,以 store 选项的方式“注入”该 store 的机制
+ store,
el: 'body',
render: (h) => h(App),
});

在子组件(Home.vue)中访问 store:

// views/Home.vue

...
<script>
export default {
created() {
console.log(this.$store.state.count);
},
};
</script>

重启服务,浏览器控制台输出 0。

axios

Tip: 参考 vue-cli 安装 axios 插件后生成的代码。

将 axios 引入工程,步骤如下:

> npm i axios@0

创建 axios.js:

// src/plugins/axios.js

import Vue from 'vue';
import axios from 'axios'; // Full config: https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; const config = {
// baseURL: process.env.baseURL || process.env.apiUrl || ""
// timeout: 60 * 1000, // Timeout
// withCredentials: true, // Check cross-site Access-Control
}; const _axios = axios.create(config); _axios.interceptors.request.use(
(config) =>
// Do something before request is sent
config,
(error) =>
// Do something with request error
Promise.reject(error)
,
); // Add a response interceptor
_axios.interceptors.response.use(
(response) =>
// Do something with response data
response,
(error) =>
// Do something with response error
Promise.reject(error)
,
); // Vue.use(Plugin) 会调用此方法
// 方法中会将 _axios 暴露给 Vue.axios、window.axios
// 并可以通过 Vue 的实例上的 axios 和 $axios 访问到 _axios
Plugin.install = function (Vue, options) {
Vue.axios = _axios;
window.axios = _axios;
Object.defineProperties(Vue.prototype, {
axios: {
get() {
return _axios;
},
},
$axios: {
get() {
return _axios;
},
},
});
}; Vue.use(Plugin); export default Plugin;

在入口文件中引入 axios.js:

// index.js
import './plugins/axios';

在 Home.vue 中使用 axios:

// Home.vue
...
<script>
export default {
created() {
console.log('this.$axios: ', this.$axios);
},
};
</script>

重启服务,浏览器控制台输出:

this.$axios:  ƒ wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
}

至此,axios.js 已成功引入到我们的工程。在 mockjs 中我们还会用到 axios。

mockjs

:在 npm 中查看 vue-cli-plugin-mock,好似需要在 vue.config.js 中配置。我们的工程不是通过 vue-cli 创建的,所以这里直接使用 mockjs。

将 mockjs 引入工程,步骤如下:

> npm i -D mockjs@1

创建 mock:

// src/mock/index.js

import Mock from 'mockjs';

// 定义了一个请求拦截
Mock.mock('/list', {
'name': '@name',
'age|10-20': 10,
'birthday': '@date("yyyy-MM-dd")'
});

在入口文件中引入 mock:

// index.js

// 会自动加载 mock 下的 index.js
import './mock'

最后测试 mock 是否能拦截 /list 请求:

// Home.vue
...
<script>
export default {
created() {
// 发送请求
this.$axios.get('/list').then(res => {
console.log('res.data: ', res.data);
})
},
};
</script>

重启服务,浏览器控制台输出如下类似数据:

res.data:  {name: "Ruth Brown", age: 14, birthday: "1973-08-18"}

Tip: 在实际项目中,mock 还需要进一步配置,比如可以在 mock/index.js 中加载 mock 文件夹中其他的 mock 文件,方便扩展。

i18n

Tip:参考 vue i18n 官方文档 和 vue-cli 安装 i18n 插件后生成的代码。

由于我们是 vue 项目,所以就选用 vue i18n。

Vue I18n 是 Vue.js 的国际化插件。它可以轻松地将一些本地化功能集成到你的 Vue.js 应用程序中。

将 i18n 引入工程,步骤如下:

> npm i vue-i18n@8

新建中英文两个 json 文件,用于存放需要翻译的字段(中文和英文),并统一放在 src/language 目录中。

// en.json
{
"apple": "Apple"
}
// zh.json
{
"apple": "苹果"
}

新建 i18n 模块,创建并导出 i18n 实例:

// src/i18n.js

import Vue from 'vue'
import VueI18n from 'vue-i18n' Vue.use(VueI18n) function loadLocaleMessages() {
// 通过 require.context() 函数来创建自己的 context
// 可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。
// 返回一个函数,函数有三个属性:resolve, keys, id。
// keys 也是一个函数,它返回一个数组,由所有可能被此 context module 处理的请求
const locales = require.context('./language', true, /\.json$/i)
const messages = {}
locales.keys().forEach(file => {
const matched = file.match(/([A-Za-z0-9-_]+)\./i)
const locale = matched[1]
messages[locale] = locales(file)
})
// messages: {en: {…}, zh: {…}}
console.log('messages: ', messages);
return messages
} export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})

在入口文件中导入 i18n 实例,并配置到 Vue 实例中:

// index.js
...
import i18n from './i18n' new Vue({
...
i18n,
el: 'body',
render: (h) => h(App),
});

接着就可以在 vue 组件中使用:

// Home.vue

<template>
<div>
i am home
<p>{{ $t("apple") }}</p>
</div>
</template>

重启服务,页面显示 ”Apple“:

Home | About
i am home
Apple

为什么显示的是英文,而非中文?由 i18n.js 中的这段代码决定:

export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})

process 是 node 中的对象,而这里是在 .js 中使用。可以这么理解:

locale: process.env.VUE_APP_I18N_LOCALE || 'en',

等效于

locale: undefined || 'en',

所以是英文。如果想在模块中使用 process.env.VUE_APP_I18N_LOCALE,还需要一些配置。

// webpack.config.js
const webpack = require('webpack'); module.exports = {
...
plugins: [
new webpack.DefinePlugin({
// DefinePlugin 会直接将内容替换了,而不是一个字符串
// 所以我们不能直接写 'zh',经常会这么写:JSON.stringify('zh')
'process.env.VUE_APP_I18N_LOCALE': JSON.stringify('zh'),
'process.env.VUE_APP_I18N_FALLBACK_LOCALE': JSON.stringify('zh'),
})
]
};

Tip: DefinePlugin 允许在编译时将你代码中的变量替换为其他值或表达式

重启服务,页面显示 ”苹果“:

Home | About
i am home
苹果

在浏览器中找到如下代码:

{\n  locale: \"zh\" || false,\n  fallbackLocale: \"zh\" || false,\n  messages: loadLocaleMessages()\n}

i18n.js 中的 process.env.VUE_APP_I18N_LOCALE 被替换成 zh。

jquery

将 jquery 引入工程,步骤如下:

> npm i -D jquery@3

配置别名:

// webpack.config.js

module.exports = {
resolve: {
alias: {
'jquery': path.resolve(__dirname, './node_modules/jquery/dist/jquery.min.js')
},
},

在需要使用的地方引用:

// Home.vue
...
<script>
import $ from 'jquery';
export default {
mounted() {
$('#app').css('color', 'blue')
},
};
</script>

重启服务,页面中文字变为蓝色,说明 jquery 引入成功。

lodash

将 lodash 引入工程,步骤如下:

> npm i -D lodash@4

在 Home.vue 中使用 lodash:

// Home.vue
...
<script>
// webpack 模块 能以各种方式表达它们的依赖关系
// 比如 es 的 import、commonjs 的 require等等
let _ = require('lodash')
export default {
mounted() {
// 调用 lodash 的 random 方法
console.log(_.random(1,10))
},
};
</script>

重启服务,浏览器控制台输出1~10之间一个随机数,说明 lodash 引入成功。

Tip: jquery 同样可以使用这种方式

其他章节请看:

vue 快速入门 系列

vue 快速入门 系列 —— vue loader 扩展的更多相关文章

  1. vue 快速入门 系列 —— vue loader 上

    其他章节请看: vue 快速入门 系列 vue loader 上 通过前面"webpack 系列"的学习,我们知道如何用 webpack 实现一个不成熟的脚手架,比如提供开发环境和 ...

  2. vue 快速入门 系列 —— vue loader 下

    其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...

  3. vue 快速入门 系列 —— vue 的基础应用(上)

    其他章节请看: vue 快速入门 系列 vue 的基础应用(上) Tip: vue 的基础应用分上下两篇,上篇是基础,下篇是应用. 在初步认识 vue一文中,我们已经写了一个 vue 的 hello- ...

  4. vue 快速入门 系列 —— vue 的基础应用(下)

    其他章节请看: vue 快速入门 系列 vue 的基础应用(下) 上篇聚焦于基础知识的介绍:本篇聚焦于基础知识的应用. 递归组件 组件是可以在它们自己的模板中调用自身的.不过它们只能通过 name 选 ...

  5. vue 快速入门 系列 —— 虚拟 DOM

    其他章节请看: vue 快速入门 系列 虚拟 DOM 什么是虚拟 dom dom 是文档对象模型,以节点树的形式来表现文档. 虚拟 dom 不是真正意义上的 dom.而是一个 javascript 对 ...

  6. vue 快速入门 系列 —— 初步认识 vue

    其他章节请看: vue 快速入门 系列 初步认识 vue vue 是什么 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架. 所谓渐进式,就是你可以一步一步.有阶段 ...

  7. vue 快速入门 系列 —— 侦测数据的变化 - [基本实现]

    其他章节请看: vue 快速入门 系列 侦测数据的变化 - [基本实现] 在 初步认识 vue 这篇文章的 hello-world 示例中,我们通过修改数据(app.seen = false),页面中 ...

  8. vue 快速入门 系列 —— 侦测数据的变化 - [vue 源码分析]

    其他章节请看: vue 快速入门 系列 侦测数据的变化 - [vue 源码分析] 本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue ...

  9. vue 快速入门 系列

    vue 快速入门(未完结,持续更新中...) 前言 为什么要学习 vue 现在主流的框架 vue.angular 和 react 都是声明式操作 DOM 的框架.所谓声明式,就是我们只需要描述状态与 ...

  10. 第二章 Vue快速入门-- 16 vue中通过属性绑定为元素绑定style行内样式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

随机推荐

  1. stm32GPIO的速度是什么意思

    [引]: I/O口输出模式下,有3种输出速度可选(2MHz.10MHz和50MHz),这个速度是指I/O口驱动电路的响应速度而不是输出信号的速度,输出信号的速度与程序有关(芯片内部在I/O口的输出部分 ...

  2. OracleHelper(对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装)

    公司的一个新项目使用ASP.NET MVC开发,经理让我写个OracleHelper,我从网上找了一个比较全的OracleHelper类,缺点是查询的时候返回DataSet,数据增删改要写很多代码(当 ...

  3. show slave各项参数解释

    how slave status 各个参数的解释 -- mysql 分类: mysql基础2012-08-23 11:03 2315人阅读 评论(0) 收藏 举报 服务器sslfilesqltable ...

  4. tp框架实现验证码验证

    //实现验证页面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  5. 什么是体数据可视化(Volume data visualization)?及体绘制的各种算法和技术的特点?

    该文对体数据进行综述,并介绍了体数据的各种算法和技术的特点. 前言 由于3D数据采集领域的高速发展,以及在具有交互式帧率的现代化工作站上执行高级可视化的可能性,体数据的重要性将继续迅速增长. 数据集可 ...

  6. iTOP-iMX6UL全能板-linux-usb-wifi的使用

    本文档介绍的是在本文档介绍的是在 Linux 系统环境下iTOP-imx6ul全能版 usb wifi 连接路由器上网 实验调试步骤.我们使用的是 imx6ul 全功能底板. 1 硬件 本文档测试使用 ...

  7. HDU 1880 魔咒词典 (Hash)

    魔咒词典 Time Limit: 8000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  8. ES6学习笔记二:各种扩展

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/7242967.html 一:字符串扩展 1:字符串遍历器 for (let char of str) { // ...

  9. 忙里偷闲写的小例子---读取android根目录下的文件或文件夹

    最近几天真的是各种意义上的忙,忙着考试,还要忙着课程设计,手上又有外包的项目,另一边学校的项目还要搞,自己的东西还在文档阶段,真的是让人想死啊!! 近半个月来,C#这方面的编码比较多,android和 ...

  10. Codeforces Round #491 (Div. 2) E - Bus Number + 反思

    E - Bus Number 最近感觉打CF各种车祸.....感觉要反思一下, 上次读错题,这次想当然地以为18!肯定暴了longlong 而没有去实践, 这个题我看到就感觉是枚举每个数字的个数,但是 ...