(已废弃)Vue学习笔记

构建项目

现在一般是使用webpack+vue的组合,如果使用的是vue-cli2.x版本,可以参考这个文章。注意该文章对现在的vue-cli3.x已经不适用了。

注意:vue-cli似乎内部自带有打包功能,因此一个vue项目,可以不依赖于webpack,自行打包发布。

vue-cli3.x的项目初始化方式如下:

安装命令行工具

注意要加上@,否则安装的vue不是最新版的。

1
2
3
4
5
6
npm install -g @vue/cli
npm install -g @vue/cli-init
// 如果项目有用到elementUI的话,执行如下这一行
npm i element-ui -S
// 注意:如果需要自定义,那么不要在gitbash中执行下面的命令,因为gitbash无法通过光标进行选择操作
vue create myProjectName

填写项目信息

跟着提示信息,一步一步填写即可。

启动项目

项目生成后,运行命令即可启动:

1
npm run serve

目录结构

vue-cli3.x生成的文件结构,相比2.x,有如下区别:

1、用public目录替代了static目录

注意,public目录下的文件,访问时,相对地址从public下面开始写。

比如public/data.js,页面中应该这样引用:src=”/data.js”

2、移除了build目录

3、入口文件index.html放在了public目录下

4、构建生成的文件放到dist目录下

引用资源

如何引用外部JS文件

开发阶段,一般需要将开发版的JS文件下载到本地再引入。

假设我们要用到的js文件叫做compass.js,我们在src下面建一个lib目录,然后放入该JS文件。注意如果JS文件没有按照模块化的方式编写,我们要手动暴露一下,即在compass.js末尾添加(注意别加default):

1
export { Compass }

然后在需要用到这个JS的.vue文件中引入:

1
import { Compass } from '../src/lib/compass'

然后在mounted方法中进行调用:

1
2
3
4
5
6
7
8
export default {
name: 'App',
mounted() {
// 调用外部JS
let chart = new CompassChart()
// TODO
},
};

如何引用外部CSS文件

和引用外部JS类似。

假设我们要用到的css文件叫做compass.css,我们在src下面建一个lib目录,然后放入该css文件。

注意CSS就不需要封装成模块了,直接在.vue文件中引用即可:

1
2
3
<style>
@import "../src/lib/compass.css";
</style>

绑定数据

记住:data是个函数,props是个key-value对象。

components是个key-value对象,不是数组。

不需要写两个大括号,直接写就行,包括内嵌JS代码也是一样:

1
2
3
<img :src="news.logo ? news.logo : 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png'" class="image">

<el-button type="text" class="button" @click="openNews(news.url)">查看详情</el-button>

如何向子组件传递数据?

直接传递固定值:

1
<Stat :total='total'/>

传递父组件的变量:

computed vs method

可以将computed叫做computed属性,这样更容易理解一些:我们是把computed当成一个属性来用的,使用的是它的返回值,即一个经过计算的动态属性值。

假如我们定义一个computed属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// a computed getter
thisIsAComputedAttribute: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
})

template中要使用computed属性,和使用data、props属性是一样的写法:

1
<span>{{ thisIsAComputedAttribute }}</span>

而method是方法,纯实现功能的,不一定有返回值,也不会当成属性来应用;如果要在template中使用method的返回值,要将其执行(注意末尾的括号()):

1
<span>{{ thisIsAMethod() }}</span>

另外computed会做变量缓存,仅仅在其计算所依赖的属性发生变更时才会重新计算;而method则每次执行都会跑一遍逻辑。

路由

应用场景:页面导航。

配置代理转发

这是vue-cli的功能,会单独起个端口服务对请求进行转发。因此查询文档的时候,不要去查Vue的文档,而是应该查询vue-cli的文档

打包

注意webpack和vue-loader的版本兼容性问题。

打包和发布,是不需要webpack的。

vue.config.js

这是vue-cli的配置文件,具体说明详见官方文档

这个配置文件是需要我们自己手动创建的,放在和src同一级的根目录下。我们经常用到的,比如@符号对应的路径,就是在这个文件里面的resolve中配置的。

如果不创建这个文件,默认打包是会将文件生成到dist目录下的;我们创建vue.config.js后,需要手动指定打包的目录为html,这是为了方便后续t hsi-cli执行上传操作。

TODO:这个不合理,应该改掉,采用默认的dist目录才对。

build

打包其实是执行了如下命令:

1
./node_modules/.bin/vue-cli-service build

这里有一个疑问:

webpack怎么引入的?看vue.config.js中有webpack相关的配置,但是package.json中并没有引入webpack。

webpack是@vue/cli-service这个包通过依赖引入的。

通过vue create projectName创建项目后,会直接将vue-cli相关的包也下载到你本地,顺带就把webpack也装上了。下面这个就是项目初始化生成的package.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
"name": "testwebpack",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^2.6.5",
"vue": "^2.6.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.12.0",
"@vue/cli-plugin-eslint": "^3.12.0",
"@vue/cli-service": "^3.12.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

与webpack整合

官方文档

调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象。该对象将会被 webpack-merge 合并入最终的 webpack 配置。

1
2
3
4
5
6
7
8
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}

可以通过vue inspect导出webpack的配置进行检查:

1
vue inspect > output.js

发布

基于thsi-cli

添加发布命令

修改package.json,在scripts下面,加入发布命令:

1
2
3
4
"scripts": {
"release": "vue-cli-service build && cd node-thsi && node app.js"
// 其他的脚本
},

拷贝node-thsi目录

将node-this拷贝到项目根目录下,和src同一级。

安装依赖包

1
2
cd node-thsi
npm i

创建静态资源目录

登录静态资源服务器,创建一个精确到小时的目录,比如2019101515.

这个目录是用于下一步发布时,上传静态资源的。

修改配置文件

修改node-thsi目录下的app.js。主要是修改node-thsi/app.js中的上传目录路径,改为上一步创建的目录;其他的一般不用怎么修改。

执行发布命令

记得要回到项目根目录下执行如下命令:

1
npm run release

其他应用

多页面应用

可以通过vue.config.js中的pages这个配置项来构建,详见官方文档

这是通过html-webpack-plugin插件来实现的。

这里给一个项目中的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pages: {
index: {
// page 的入口
entry: 'src/pages/index/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
}
}

动态组件

当我们不能预知具体用什么组件来渲染页面时,可以考虑动态组件的方案。

比如我要在页面上渲染一行文本,但是这行文本可能有时候会渲染成标题(比如h2)的样式,有时候又会渲染为普通文本(比如span)的样式,这种场景就很适合用动态组件。

注意使用动态组件时,需要将所有相关的组件都提前import进来。

样式穿透

>>>:将父组件的样式穿透到子组件中。

这个和less/sass中的/deep/是类似的功能。

常见错误

vue.esm.js?efeb:628 [Vue warn]: Property or method “newsList” is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

这个问题卡了我很久,结果发现是因为我copy了element的示例代码,其代码末尾已经有export default {}这些js代码了,而我没注意到,自己在template和style中间又写了一段script脚本,导致我写的这段js没有运行,因此报了上述错误。

[Vue warn]: 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-cli升级后,写法变更导致的,具体可以参考这个文章:https://blog.csdn.net/wxl1555/article/details/83187647

vue.runtime.esm.js?2b0e:619 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value.

不能直接修改组件的props中的属性。如果一定要修改(或者说是有向父组件传递数据的需求),可以通过如下方式实现:

1
2
3
4
父组件在调用子组件的地方,通过v-on:或者@绑定事件
子组件通过this.$emit(methodName, params)触发事件(抛出)
父组件响应事件,修改属性(接住)
(可选)触发属性的watch方法

摘自别人的博客:如果父组件的变量想和子组件的临时变量数据同步,那么就需要用watch对该临时变量进行监听,然后通过this.$emit()来通知父组件数据的同步。

vue-cli的版本问题

vue-cli2.x和3.x区别比较大,现在一般都是用3.x了,配置文件简化为一个vue.config.js。

vue2.x的配置文件是在build目录下的。

vue-dev-tools怎么没在Chrome控制台显示出来?

1、压根没装Chrome扩展

2、当前页面不是用Vue写的

3、是在打开控制台之后安装的dev-tool,这种情况重新开一下控制台就可以了

为什么我修改了data的值,但是页面上没有自动跟着变动?

这是我遇到的一个案例,流程如下:

1、初始化页面,渲染组件A和B,其中A内部引用了B,A会向B传递数据

2、在A中执行了替换页面DOM结构的操作

3、修改A传递给B的属性,发现页面上B没有自动变更

原因是因为我修改了DOM元素,导致一开始和数据绑定的DOM在内存中已经不存在了,页面上展示的B其实是另外一个组件示例,也就是另外一个内存引用,已经没有和之前的A的属性绑定了,自然就不会变更了。

解决方案:

我在A中渲染B的地方加了一个v-if判断,仅当属性不为0时才渲染B:

1
<B v-if="total" :total="total"/>

然后在A中,先清空这个total属性,等DOM节点操作完成后,再赋值给total属性,重新绘制B组件:

1
2
3
4
5
6
mounted() {
this.total = 0;
// 处理DOM节点
// TODO
this.total = 456;
}

参考Vue官方文档对于v-if的介绍:

1
2
3
4
5
6
7
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

如何在页面上动态添加组件?

基本上是不需要动态添加组件的,应该在页面一开始就把所有组件预设好。

如果是初始不显示的组件,可以通过v-if、v-show等不显示出来。

如何将数组绑定到v-model?

我遇到的场景,是页面上点击一个添加按钮,会生成一条例句输入框。

我给组件设置了2个data属性:

sentences: [] // 用来存放输入框中用户真正录入的数据

sentenceTextAreaNumber: 1 // 用来存放页面的数据库数量,点击添加/删除按钮的时候,动态调整这个数据,进而达到动态增删输入框的目的。

v-for的注意事项

如果你使用v-for的场景,涉及元素的增加和删除,那么一定要谨慎确认key的值,不能直接用数据的index作为key,否则可能会因为数据变更后,通过index定位到了错误的页面元素,导致页面渲染出错。

牢记一个原则:

根据不可能发生变更的属性,去唯一定位页面元素。

v-if和v-show的误用

(TODO)Vue3+TypeScript一文弄懂

Vue3+TypeScript?看这一篇就够了_淮城一只猫的博客-CSDN博客_typescript vue3