add:数组的常见方法
This commit is contained in:
452
12-Vue基础/00-Vue的介绍和vue-cli.md
Normal file
452
12-Vue基础/00-Vue的介绍和vue-cli.md
Normal file
@@ -0,0 +1,452 @@
|
||||
|
||||
|
||||
|
||||
## MVVM模式
|
||||
|
||||

|
||||
|
||||
- Model:负责数据存储
|
||||
|
||||
- View:负责页面展示
|
||||
|
||||
- View Model:负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
|
||||
|
||||
|
||||
## 关于框架
|
||||
|
||||
|
||||
### 为什么要学习流行框架
|
||||
|
||||
**1、企业为了提高开发效率**:在企业中,时间就是效率,效率就是金钱;企业中,使用框架,能够提高开发的效率。
|
||||
|
||||
|
||||
**提高开发效率的发展历程**:
|
||||
|
||||
原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)
|
||||
|
||||
|
||||
2、在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑。
|
||||
|
||||
|
||||
|
||||
**3、增强自己就业时候的竞争力**:
|
||||
|
||||
- 人无我有,人有我优。
|
||||
|
||||
- 你平时不忙的时候,都在干嘛?
|
||||
|
||||
|
||||
|
||||
### 框架和库的区别
|
||||
|
||||
|
||||
**框架**:
|
||||
|
||||
框架是一套完整的解决方案。
|
||||
|
||||
对项目的**侵入性**较大,项目如果需要更换框架,则需要重新架构整个项目。但是优点也很明显:功能完善、提供了一整套的解决方案。
|
||||
|
||||
|
||||
|
||||
|
||||
**库(插件)**
|
||||
|
||||
只是提供某一个小功能。
|
||||
|
||||
对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。
|
||||
|
||||
举例:
|
||||
|
||||
- 从Jquery 切换到 Zepto
|
||||
|
||||
- 从 EJS 切换到 art-template
|
||||
|
||||
|
||||
|
||||
## 前端的各种框架
|
||||
|
||||
### Vue、React、AngularJS
|
||||
|
||||
AngularJS 提供更多的是一套解决方案,更像是一个生态。
|
||||
|
||||
Vue 和 React目前都是用了 Virtual Dom。
|
||||
|
||||
### Vue 和 React 的对比
|
||||
|
||||

|
||||
|
||||
|
||||
Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架。
|
||||
|
||||
Vue.js 是一套构建用户界面的框架,**只关注视图层**。
|
||||
|
||||
|
||||
|
||||
### Vue 和 React 的相同点
|
||||
|
||||

|
||||
|
||||
国内的很多开发者喜欢 Vue;国外的开发者更喜欢 React,适合做大型网站。
|
||||
|
||||
### 什么是虚拟 DOM
|
||||
|
||||
传统的web开发,是利用 jQuery操作DOM,这是非常耗资源的。
|
||||
|
||||
我们可以在 JS 的内存里构建类似于DOM的对象,去拼装数据,拼装完整后,把数据整体解析,一次性插入到html里去。这就形成了虚拟 DOM。
|
||||
|
||||
|
||||
Vue1.0没有虚拟DOM,Vue2.0改成了基于虚拟DOM。
|
||||
|
||||
|
||||
### 前端框架回顾
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Vue框架中,没有控制器。
|
||||
|
||||
|
||||
## Vue 框架
|
||||
|
||||
### 发展历史
|
||||
|
||||

|
||||
|
||||
2016.10发布2.0版本。
|
||||
|
||||
### 相关网址
|
||||
|
||||
- [中文官网](https://cn.vuejs.org/)
|
||||
|
||||
|
||||
- [vuejs官方论坛](https://forum.vuejs.org/)
|
||||
|
||||
- GitHub地址:<https://github.com/vuejs/vue>
|
||||
|
||||
|
||||
- Vue1.0 在线文档:<http://v1-cn.vuejs.org/guide/>
|
||||
|
||||
- Vue2.x 在线文档:<https://cn.vuejs.org/v2/guide/>
|
||||
|
||||
- Vue1下载地址:<http://v1-cn.vuejs.org/js/vue.js>
|
||||
|
||||
- Vue2下载地址:<https://cdn.jsdelivr.net/npm/vue/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
上方截图的时间:2018-03-02。
|
||||
|
||||
|
||||
|
||||
### 介绍
|
||||
|
||||
Vue 本身并不是一个框架,Vue结合周边生态构成一个灵活的、渐进式的框架。
|
||||
|
||||
渐进式框架图:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
- 声明式渲染:当我们在页面里,通过 new 一个 view 的事例时,
|
||||
|
||||
|
||||
### Vue 的特点
|
||||
|
||||
- 模板渲染
|
||||
|
||||
- 组件化/模块化
|
||||
|
||||
- 扩展功能:路由、ajax、数据流等。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Vue 的环境搭建
|
||||
|
||||
> 我们首先要安装好 NVM、Node.js环境,然后再来做下面的操作。
|
||||
|
||||
### 常见的插件
|
||||
|
||||
- Webpack:代码模块化构建打包工具。
|
||||
|
||||
- Gulp:基于流的自动化构建工具。
|
||||
|
||||
- Grunt :JS 世界的构建工具。
|
||||
|
||||
- Babel:使用最新的 规范来编写 js。
|
||||
|
||||
- Vue:构建数据驱动的Web界面的渐进式框架
|
||||
|
||||
- Express:基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
|
||||
|
||||
以上这些包,都可以通过 NPM 这个包管理工具来安装。
|
||||
|
||||
### 引用 Vue.js 文件
|
||||
|
||||
1、**方式一**:(引用的方式)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
2、方式二:(下载 vue.js 文件)
|
||||
|
||||
去网站<https://cdn.jsdelivr.net/npm/vue/>下载 vue.js 文件,直接放到工程文件里,然后引用。
|
||||
|
||||
3、方式三:(npm安装vue)
|
||||
|
||||
```bash
|
||||
# 最新稳定版
|
||||
$ npm install vue
|
||||
```
|
||||
|
||||
如果网络不稳定,可以采用下面的方式安装:
|
||||
|
||||
```
|
||||
$ cnpm i vue --save
|
||||
```
|
||||
|
||||
我们可以看到 vue.js 的安装目录:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
此时在 src 中需要引入的路径是:
|
||||
|
||||
|
||||

|
||||
|
||||
上图中的引入方式,不是很理解。
|
||||
|
||||
### 安装 vue-cli(命令行工具)
|
||||
|
||||
Vue 提供一个官方命令行工具,可用于快速搭建大型单页应用。该工具为现代化的前端开发工作流提供了开箱即用的构建配置。只需几分钟即可创建并启动一个带热重载、保存时静态检查以及可用于生产环境的构建配置的项目。
|
||||
|
||||
安装命令如下:
|
||||
|
||||
```bash
|
||||
# 全局安装 vue-cli
|
||||
$ npm install -g vue-cli
|
||||
```
|
||||
|
||||
以下代码来自官网,仅供阅读:
|
||||
|
||||
```
|
||||
# 创建一个基于 webpack 模板的简单的新项目【荐】
|
||||
$ vue init webpack-simple demo01
|
||||
|
||||
# 创建一个基于 webpack 模板的完整的新项目
|
||||
$ vue init webpack demo02
|
||||
|
||||
|
||||
# 安装依赖,走你
|
||||
$ cd my-project
|
||||
$ npm install
|
||||
$ npm run dev
|
||||
```
|
||||
|
||||
## 利用 vue-cli 新建一个空的项目
|
||||
|
||||
### 构建一个 simple 项目
|
||||
|
||||
我们利用 vue-cli 来构建一个 SPA 应用:
|
||||
|
||||
(1)首先执行:
|
||||
|
||||
```
|
||||
$ vue init webpack-simple VueDemo01
|
||||
|
||||
```
|
||||
|
||||
然后根据提示输入 project name(**要求小写**),description 默认即可:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
上方截图中,`npm install `指的是下载各种依赖包,`npm run dev`指的是打开发包,`npm run build`指的是打生产包。
|
||||
|
||||
我们可以看到这个项目的结构:
|
||||
|
||||

|
||||
|
||||
- src:项目源码
|
||||
|
||||
- .babelrc:ES6编译插件的配置
|
||||
|
||||
- index.html:单页面的入口
|
||||
|
||||
|
||||
(2)Mac环境,安装各种依赖包,执行如下命令:
|
||||
|
||||
```
|
||||
cd /Users/smyhvae/Dropbox/workspace/Mac/VueDemo01
|
||||
|
||||
cnpm install
|
||||
```
|
||||
|
||||
备注:我们在 GitHub上下载的任何Vue有关的项目,第一步都是要首先执行 cnpm install。
|
||||
|
||||
|
||||
(3)让项目跑起来:
|
||||
|
||||
```
|
||||
cnpm run dev
|
||||
```
|
||||
|
||||
这个空的项目就可以在浏览器上跑起来:
|
||||
|
||||
|
||||

|
||||
|
||||
如果是在webstorm中开发这个项目,会有点卡,因为依赖的包比较多,重启软件即可。
|
||||
|
||||
### 构建一个 非 simple 项目
|
||||
|
||||
|
||||
(1)构建一个空的项目,首先执行:
|
||||
|
||||
```
|
||||
$ vue init webpack VueDemo02
|
||||
|
||||
```
|
||||
|
||||
然后根据提示依次输入:
|
||||
|
||||
- project name:**要求小写**。
|
||||
|
||||
- description:默认即可。
|
||||
|
||||
- vue-router:需要。
|
||||
|
||||
- ESlint:语法检查,初学者可以暂时不需要。
|
||||
|
||||
- 单元测试:暂时也不需要。
|
||||
|
||||
- e2e test:不需要。
|
||||
|
||||
|
||||
|
||||
(2)Mac环境,安装各种依赖包,执行如下命令:
|
||||
|
||||
```
|
||||
cd /Users/smyhvae/Dropbox/workspace/Mac/VueDemo02
|
||||
|
||||
npm install
|
||||
```
|
||||
|
||||
备注:我们在 GitHub上下载的任何Vue有关的项目并没有包含 `node_modules`,第一步都是要首先执行 cnpm install,把 `node_modules`里的包下载下来。我们发给同事的工程文件,建议也不要包含 `node_modules`。
|
||||
|
||||
|
||||
|
||||
(3)让项目跑起来:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
这个空的项目就可以在浏览器上跑起来。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## vue 项目结构分析
|
||||
|
||||

|
||||
|
||||
- buid:打包配置的文件夹
|
||||
|
||||
- config:webpack对应的配置
|
||||
|
||||
- src:开发项目的源码
|
||||
- App.vue:入口组件。`.vue`文件都是组件。
|
||||
- main.js:项目入口文件。
|
||||
|
||||
- static:存放静态资源
|
||||
|
||||
- `.babelrc`:解析ES6的配置文件
|
||||
|
||||
- `.editorcofnig`:编辑器的配置
|
||||
|
||||
- `.postcssrc.js`:html添加前缀的配置
|
||||
|
||||
- `index.html`:单页面的入口。通过 webpack打包后,会把 src 源码进行编译,插入到这个 html 里面来。
|
||||
|
||||
|
||||
- `package.json`:项目的基础配置,包含版本号、脚本命令、项目依赖库、开发依赖库、引擎等。
|
||||
|
||||
|
||||
|
||||
|
||||
### 图片的base64编码
|
||||
|
||||
默认是10k以下,建议都通过 base64编码。在配置文件`webpack.base.conf.js`中进行修改:
|
||||
|
||||
```
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||
}
|
||||
```
|
||||
|
||||
## Vue 基础语法
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
本文参考链接:
|
||||
|
||||
- [Get全栈技能点 Vue2.0/Node.js/MongoDB 打造商城系统](https://coding.imooc.com/class/chapter/113.html)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
659
12-Vue基础/01-01.Vue的系统指令.md
Normal file
659
12-Vue基础/01-01.Vue的系统指令.md
Normal file
@@ -0,0 +1,659 @@
|
||||
|
||||
> 本文最初发表于[博客园](),并在[GitHub](https://github.com/smyhvae/Web)上持续更新**前端的系列文章**。欢迎在GitHub上关注我,一起入门和进阶前端。
|
||||
|
||||
> 以下是正文。
|
||||
|
||||
|
||||
## 本文主要内容
|
||||
|
||||
- 插值表达式 {{}}
|
||||
|
||||
- v-cloak
|
||||
|
||||
- v-text
|
||||
|
||||
- v-html
|
||||
|
||||
- v-bind
|
||||
|
||||
- v-on
|
||||
|
||||
- 举例:文字滚动显示(跑马灯效果)
|
||||
|
||||
- v-on的事件修饰符
|
||||
|
||||
|
||||
|
||||
## Vue初体验
|
||||
|
||||
新建一个空的项目,引入vue.js文件。写如下代码:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<!--1、导入Vue的包-->
|
||||
<script src="vue2.5.15.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--这个div区域就是MVVM中的 View-->
|
||||
<div id="div1">
|
||||
{{name}}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
// 2、创建一个Vue的实例
|
||||
//new出来的对象就是MVVM中的 View Module(调度者)
|
||||
var myVue = new Vue({
|
||||
el: '#div1', //当前vue对象将接管上面的div1区域
|
||||
data: {//data就是MVVM中的 module
|
||||
name: 'smyhvae'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
显示效果:
|
||||
|
||||

|
||||
|
||||
如果我们在控制台输入`myVue.$data.name = 'haha'`,页面会**自动更新**name的值。意思是,当我们直接修改data数据,页面会自动更新,而不用去操作DOM。
|
||||
|
||||
|
||||
下面来讲一下Vue的各种系统指令。
|
||||
|
||||
|
||||
## 插值表达式 {{}}
|
||||
|
||||
数据绑定最常见的形式就是使用 “Mustache” 语法(双大括号)的文本插值。例如:
|
||||
|
||||
```html
|
||||
<span>Message: {{ msg }}</span>
|
||||
```
|
||||
|
||||
Mustache 标签将会被替代为对应数据对象上 msg 属性(msg定义在data对象中)的值。
|
||||
无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会**自动更新**。
|
||||
|
||||
`{{}}`对JavaScript 表达式支持,例如:
|
||||
|
||||
```javascript
|
||||
{{ number + 1 }}
|
||||
|
||||
{{ ok ? 'YES' : 'NO' }}
|
||||
|
||||
{{ name == 'smyhvae' ? 'true' : 'false' }}
|
||||
|
||||
{{ message.split('').reverse().join('') }}
|
||||
```
|
||||
|
||||
|
||||
但是有个限制就是,每个绑定都**只能包含单个表达式**,如下表达式无效:
|
||||
|
||||
```html
|
||||
<!-- 这是语句,不是表达式 -->
|
||||
{{ var a = 1 }}
|
||||
|
||||
<!-- 流控制也不会生效,请使用三元表达式 -->
|
||||
{{ if (ok) { return message } }}
|
||||
```
|
||||
|
||||
|
||||
代码举例:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<span>content:{{name}}</span>
|
||||
</div>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: 'smyhvae'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## v-cloak
|
||||
|
||||
`v-cloak`:保持和元素实例的关联,直到结束编译后自动消失。
|
||||
|
||||
v-cloak指令和CSS 规则一起用的时候,能够**解决差值表达式闪烁的问题**(即:可以隐藏未编译的标签直到实例准备完毕)。
|
||||
|
||||
就拿上一段代码来举例,比如说,`{{name}}`这个内容,**在网速很慢的情况下,一开始会直接显示`{{name}}`这个内容**,等网络加载完成了,才会显示`smyhvae`。那这个**闪烁的问题**该怎么解决呢?
|
||||
|
||||
解决办法是:通过`v-cloak`隐藏`{{name}}`这个内容,当加载完毕后,再显示出来。
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
|
||||
/*2、在样式表里设置:只要是有 v-cloak 属性的标签,我都让它隐藏。
|
||||
直到 Vue实例化完毕以后,v-cloak 会自动消失,那么对应的css样式就会失去作用,最终将span中的内容呈现给用户 */
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 1、给 span 标签添加 v-cloak 属性 -->
|
||||
<span v-cloak>{{name}}</span>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: 'smyhvae'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||
## v-text
|
||||
|
||||
v-text可以将一个变量的值渲染到指定的元素中。例如:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<!--vue的版本:2.5.15-->
|
||||
<script src="vue.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="div1">
|
||||
<span v-text="name"></span>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#div1',
|
||||
data: {
|
||||
name: 'hello smyhvae'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
|
||||

|
||||
|
||||
### 差值表达式和 v-text 的区别
|
||||
|
||||
```html
|
||||
<!-- 差值表达式 -->
|
||||
<span>content:{{name}}</span>
|
||||
|
||||
<!-- v-text -->
|
||||
<span v-text="name">/span>
|
||||
```
|
||||
|
||||
**区别1**: v-text 没有闪烁的问题,因为它是放在属性里的。
|
||||
|
||||
**区别2** :插值表达式只会替换自己的这个占位符,并不会把整个元素的内容清空。v-text 会**覆盖**元素中原本的内容。
|
||||
|
||||
为了解释区别2,我们来用代码举例:
|
||||
|
||||
```html
|
||||
<!-- 差值表达式 -->
|
||||
<p>content:++++++{{name}}------</p>
|
||||
|
||||
<!-- v-text -->
|
||||
<p v-text="name">------++++++</p>
|
||||
```
|
||||
|
||||
上方代码的演示结果:
|
||||
|
||||

|
||||
|
||||
其实,第二行代码中,只要浏览器中还没有解析到`v-text="name"`的时候,会显示`------++++++`;当解析到`v-text="name"`的时候,name的值会直接替换`------++++++`。
|
||||
|
||||
|
||||
## v-html
|
||||
|
||||
|
||||
`v-text`是纯文本,而`v-html`会被解析成html元素。
|
||||
|
||||
注意:使用v-html渲染数据可能会非常危险,因为它很容易导致 XSS(跨站脚本) 攻击,使用的时候请谨慎,能够使用{{}}或者v-text实现的不要使用v-html。
|
||||
|
||||
代码举例:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<p>{{msg}}</p>
|
||||
<p v-text="msg"></p>
|
||||
<p v-html="msg"></p>
|
||||
|
||||
</div>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '<h1>我是一个大大的h1标题</h1>'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||

|
||||
|
||||
|
||||
## v-bind:属性绑定机制
|
||||
|
||||
`v-bind`:用于绑定**属性**。
|
||||
|
||||
比如说:
|
||||
|
||||
```html
|
||||
<img v-bind:src="imageSrc +'smyhvaeString'">
|
||||
|
||||
<div v-bind:style="{ fontSize: size + 'px' }"></div>
|
||||
```
|
||||
|
||||
|
||||
上方代码中,给属性加了 v-bind 之后,属性值里的整体内容是**表达式**,属性值里的`imageSrc`和`size`是Vue实例里面的**变量**。
|
||||
|
||||
也就是说, v-bind的属性值里,可以写合法的 js 表达式。
|
||||
|
||||
上面两行代码也可以简写成:
|
||||
|
||||
```html
|
||||
<img :src="imageSrc">
|
||||
|
||||
<div :style="{ fontSize: size + 'px' }"></div>
|
||||
```
|
||||
|
||||
**举例:**
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="div1">
|
||||
<!-- value里的值只是简单的字符串 -->
|
||||
<input type="text" value="name">
|
||||
<!-- 加上 v-bind 之后,value里的值是 Vue 里的变量 -->
|
||||
<input type="text" v-bind:value="name">
|
||||
<!-- 超链接后面的path是 Vue 里面的变量 -->
|
||||
<a v-bind="{href:'http://www.baidu.com/'+path}">超链接</a>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="vue.js"></script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#div1',
|
||||
data: {
|
||||
name: 'smyhvae',
|
||||
path: `2.html`
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
上面的代码中,我们给`value`这个属性绑定了值,此时这个值是一个变量。
|
||||
|
||||
效果:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## v-on:事件绑定机制
|
||||
|
||||
### `v-on:click`:点击事件
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<!--vue的版本:2.5.15-->
|
||||
<script src="vue.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--这个div区域就是MVVM中的 View-->
|
||||
<div id="div1">
|
||||
<!-- 给button节点绑定按钮的点击事件 -->
|
||||
{{name}}
|
||||
<button v-on:click="change">改变name的值</button>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
//new出来的对象就是MVVM中的 View Module
|
||||
var myVue = new Vue({
|
||||
el: '#div1', //当前vue对象将接管上面的div区域
|
||||
data: { //data就是MVVM中的 module
|
||||
name: 'smyhvae'
|
||||
},
|
||||
|
||||
//注意,下方这个 `methods` 是Vue中定义方法的关键字,不能改
|
||||
//这个 methods 属性中定义了当前Vue实例所有可用的方法
|
||||
methods: {
|
||||
change: function() { //上面的button按钮的点击事件
|
||||
this.name += '1';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
上方代码中,我们给button按钮绑定了点击事件。注意,这个button标签要写在div区域里(否则点击事件不生效),因为下方的View module接管的是div区域。
|
||||
|
||||
### `v-on`的简写形式
|
||||
|
||||
例如:
|
||||
|
||||
```html
|
||||
<button v-on:click="change">改变name的值</button>
|
||||
```
|
||||
|
||||
可以简写成:
|
||||
|
||||
```html
|
||||
<button @click="change">改变name的值</button>
|
||||
```
|
||||
|
||||
|
||||
### v-on的常用事件
|
||||
|
||||
v-on 提供了click 事件,也提供了一些其他的事件。
|
||||
|
||||
|
||||
- v-on:click
|
||||
|
||||
- v-on:keydown
|
||||
|
||||
- v-on:keyup
|
||||
|
||||
- v-on:mousedown
|
||||
|
||||
- v-on:mouseover
|
||||
|
||||
- v-on:submit
|
||||
|
||||
- ....
|
||||
|
||||
## 举例:文字滚动显示(跑马灯效果)
|
||||
|
||||
我们利用上面几段所学的内容,做个跑马灯的小例子。要实现的效果是:类似于LED屏幕上,滚动显示的文字。
|
||||
|
||||
**文字滚动显示的思路**:
|
||||
|
||||
(1)每次点击按钮后,拿到 msg 字符串,然后调用字符串的`substring`来进行字符串的截取操作,把第一个字符截取出来,放到最后一个位置即可。这就实现了滚动的效果。
|
||||
(2)为了实现文字**自动连续滚动**的效果,需要把步骤(1)中点击按钮的操作,放到**定时器**中去。
|
||||
|
||||
我们先来看一下 点击事件里的代码改怎么写。
|
||||
|
||||
**步骤 1**:每次点击按钮,字符串就滚动一次。代码如下:
|
||||
|
||||
```javascript
|
||||
startMethod: function () {
|
||||
// 获取 msg 的第一个字符
|
||||
var start = this.msg.substring(0, 1);
|
||||
// 获取 后面的所有字符
|
||||
var end = this.msg.substring(1);
|
||||
// 重新拼接得到新的字符串,并赋值给 this.msg
|
||||
this.msg = end + start;
|
||||
}
|
||||
```
|
||||
|
||||
**步骤2**:给上面的操作添加定时器。代码如下:
|
||||
|
||||
```javascript
|
||||
startMethod: function () {
|
||||
var _this = this;
|
||||
//添加定时器:点击按钮后,让字符串连续滚动
|
||||
setInterval(function () {
|
||||
// 获取 msg 的第一个字符
|
||||
var start = _this.msg.substring(0, 1);
|
||||
// 获取 后面的所有字符
|
||||
var end = _this.msg.substring(1);
|
||||
// 重新拼接得到新的字符串,并赋值给 this.msg
|
||||
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去
|
||||
_this.msg = end + start;
|
||||
console.log(_this.msg);
|
||||
}, 400);
|
||||
}
|
||||
```
|
||||
|
||||
上面的代码中,我们发现,如果在定时器中直接使用this,这个this指向的是window。为了解决这个问题,我们是通过`_this`来解决了这个问题。
|
||||
|
||||
另外,我们还可以利用箭头函数来解决this指向的问题,因为箭头函数总的this指向,会继承外层函数的this指向。如下。
|
||||
|
||||
**步骤2的改进版**:用箭头函数来改进定时器,解决this指向的问题。代码如下:
|
||||
|
||||
```javascript
|
||||
startMethod: function () {
|
||||
//添加定时器:点击按钮后,让字符串连续滚动
|
||||
setInterval(() => {
|
||||
// 获取 msg 的第一个字符
|
||||
var start = this.msg.substring(0, 1);
|
||||
// 获取 后面的所有字符
|
||||
var end = this.msg.substring(1);
|
||||
// 重新拼接得到新的字符串,并赋值给 this.msg
|
||||
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去
|
||||
this.msg = end + start;
|
||||
console.log(_this.msg);
|
||||
}, 400);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**步骤3**:停止定时器。如下:
|
||||
|
||||
我们还需要加一个按钮,点击按钮后,停止文字滚动。也就是停止定时器。
|
||||
|
||||
提示:我们最好把定时器的id放在全局的位置(放到data里),这样的话,开启定时器的方法和停止定时器的方法,都可以同时访问到这个定时器。
|
||||
|
||||
代码如下:
|
||||
|
||||
```javascript
|
||||
data: {
|
||||
msg: '生命壹号,永不止步~~~',
|
||||
intervalId: null
|
||||
},
|
||||
methods: {
|
||||
startMethod: function () {
|
||||
//添加定时器:点击按钮后,让字符串连续滚动
|
||||
this.intervalId = setInterval(() => { //【注意】这个定时器的this,一定不要忘记了
|
||||
// 获取 msg 的第一个字符
|
||||
var start = this.msg.substring(0, 1);
|
||||
// 获取 后面的所有字符
|
||||
var end = this.msg.substring(1);
|
||||
// 重新拼接得到新的字符串,并赋值给 this.msg
|
||||
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去
|
||||
this.msg = end + start;
|
||||
console.log(_this.msg);
|
||||
}, 400);
|
||||
},
|
||||
|
||||
stopMethod: function () {
|
||||
//停止定时器:点击按钮后,停止字符串的滚动
|
||||
clearInterval(this.intervalId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**【重要】步骤4**:一开始的时候,还需要判断是否已经存在定时器。如下:
|
||||
|
||||
步骤3中的代码,虽然做了停止定时器的操作,但是有个问题:在连续多次点击“启动定时器”按钮的情况下,此时再点击“停止定时器”的按钮,是没有反应的。因此,我们需要改进的地方是:
|
||||
|
||||
- **在开启定时器之前,先做一个判断**:如果定时器不为 null,就不继续往下执行了(即不再开启新的定时器),防止开启了多个定时器。
|
||||
|
||||
- **停止定时器的时候,虽然定时器停止了,但定时器并不为 null**。因此,最后我们还需要手动将定时器设置为null。这样,才能恢复到最初始的状态。
|
||||
|
||||
**完整版代码**:
|
||||
|
||||
针对上面的四个步骤,为了实现这个案例,完整版代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<p>{{msg}}</p>
|
||||
<input type="button" value="跑马灯走起" @click="startMethod">
|
||||
<input type="button" value="跑马灯停止" @click="stopMethod">
|
||||
|
||||
</div>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '生命壹号,永不止步~~~',
|
||||
intervalId: null
|
||||
},
|
||||
methods: {
|
||||
startMethod: function () {
|
||||
|
||||
//【重要】在开启定时器之前,先进行判断,避免出现多个定时器
|
||||
//如果出现了多个定时器,后面的“停止定时器”操作是无效的
|
||||
if (this.intervalId != null) return; //【注意】这个定时器的this,一定不要忘记了
|
||||
|
||||
//添加定时器:点击按钮后,让字符串连续滚动
|
||||
this.intervalId = setInterval(() => {
|
||||
// 获取 msg 的第一个字符
|
||||
var start = this.msg.substring(0, 1);
|
||||
// 获取 后面的所有字符
|
||||
var end = this.msg.substring(1);
|
||||
// 重新拼接得到新的字符串,并赋值给 this.msg
|
||||
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去
|
||||
this.msg = end + start;
|
||||
console.log(this.msg);
|
||||
}, 400);
|
||||
},
|
||||
|
||||
stopMethod: function () {
|
||||
// 停止定时器:点击按钮后,停止字符串的滚动
|
||||
clearInterval(this.intervalId);
|
||||
// 每当清除了定时器之后,需要重新把 intervalId 置为 null
|
||||
this.intervalId = null;
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
**上方代码的总结**:
|
||||
|
||||
- 在Vue的实例中,如果想要获取data里的属性、methods里面的方法,都要通过`this`来访问。这里的**this指向的是Vue的实例对象**。
|
||||
|
||||
- VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把最新的数据,从 data 上同步到页面中去。这样做 的好处是:**程序员只需要关心数据,不需要考虑如何重新渲染DOM页面;减少DOM操作**。
|
||||
|
||||
- 在调用定时器 id 的时候,代码是`this.intervalId`,这个`this`一定不要漏掉了。
|
||||
|
||||
|
||||
## 我的公众号
|
||||
|
||||
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。
|
||||
|
||||
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
|
||||
|
||||

|
||||
|
||||
263
12-Vue基础/01-02.v-on的事件修饰符.md
Normal file
263
12-Vue基础/01-02.v-on的事件修饰符.md
Normal file
@@ -0,0 +1,263 @@
|
||||
|
||||
## v-on的事件修饰符
|
||||
|
||||
### v-on的常见事件修饰符
|
||||
|
||||
`v-on` 提供了很多事件修饰符来辅助实现一些功能。事件修饰符有如下:
|
||||
|
||||
- `.stop` 阻止冒泡。本质是调用 event.stopPropagation()。
|
||||
|
||||
- `.prevent` 阻止默认事件(默认行为)。本质是调用 event.preventDefault()。
|
||||
|
||||
- `.capture` 添加事件监听器时,使用捕获的方式(也就是说,事件采用捕获的方式,而不是采用冒泡的方式)。
|
||||
|
||||
- `.self` 只有当事件在该元素本身(比如不是子元素)触发时,才会触发回调。
|
||||
|
||||
- `.once` 事件只触发一次。
|
||||
|
||||
- ``.{keyCode | keyAlias}` 只当事件是从侦听器绑定的元素本身触发时,才触发回调。
|
||||
|
||||
- ``.native` 监听组件根元素的原生事件。
|
||||
|
||||
PS:一个事件,允许同时使用多个事件修饰符。
|
||||
|
||||
写法示范:
|
||||
|
||||
```html
|
||||
<!-- click事件 -->
|
||||
<button v-on:click="doThis"></button>
|
||||
|
||||
<!-- 缩写 -->
|
||||
<button @click="doThis"></button>
|
||||
|
||||
<!-- 内联语句 -->
|
||||
<button v-on:click="doThat('hello', $event)"></button>
|
||||
|
||||
<!-- 阻止冒泡 -->
|
||||
<button @click.stop="doThis"></button>
|
||||
|
||||
<!-- 阻止默认行为 -->
|
||||
<button @click.prevent="doThis"></button>
|
||||
|
||||
<!-- 阻止默认行为,没有表达式 -->
|
||||
<form @submit.prevent></form>
|
||||
|
||||
<!-- 串联修饰符 -->
|
||||
<button @click.stop.prevent="doThis"></button>
|
||||
```
|
||||
|
||||
|
||||
### `.stop`的举例
|
||||
|
||||
我们来看下面这个例子:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<style>
|
||||
.father {
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
background: pink;
|
||||
}
|
||||
|
||||
.child {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="father" @click="fatherClick">
|
||||
<div class="child" @click="childClick">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
methods: {
|
||||
fatherClick: function () {
|
||||
console.log('father 被点击了');
|
||||
},
|
||||
childClick: function () {
|
||||
console.log('child 被点击了');
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
上方代码中,存在冒泡的现象,父标签中包含了一个子标签。当点击子标签时,父标签也会被触发。打印顺序是:
|
||||
|
||||
```
|
||||
child 被点击了
|
||||
father 被点击了
|
||||
```
|
||||
|
||||
那么问题来了,如果我不想让子标签的点击事件冒泡到父亲,该怎么做呢?办法是:给子标签加一个**事件修饰符**`.stop`,阻止冒泡。代码如下:
|
||||
|
||||
```html
|
||||
<div class="child" @click.stop="childClick">
|
||||
```
|
||||
|
||||
阻止冒泡后,当点击子标签时,打印结果是:
|
||||
|
||||
```
|
||||
child 被点击了
|
||||
```
|
||||
|
||||
PS:我发现一个有意思的现象。上方的这行代码中,如果把`.stop`改为`:stop`,造成的现象是,父标签被触发了,而子标签没有被触发。
|
||||
|
||||
### `.capture`举例
|
||||
|
||||
`.capture`:触发事件时,采用捕获的形式,而不是冒泡的形式。
|
||||
|
||||
还是采用上面的例子:当按钮点击时,如果想要采取捕获的方式,而不是冒泡的方式,办法是:可以直接在父标签上加事件修饰符`.capture`。代码如下:
|
||||
|
||||
```html
|
||||
<div class="father" @click.capture="fatherClick">
|
||||
```
|
||||
|
||||
当点击子标签时,打印结果是:
|
||||
|
||||
```
|
||||
father 被点击了
|
||||
child 被点击了
|
||||
```
|
||||
|
||||
|
||||
### `.prevent`的举例1
|
||||
|
||||
比如说,超链接`<a>`默认有跳转行为,那我可以通过事件修饰符`.prevent`阻止这种跳转行为。
|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 通过 .prevent 阻止超链接的默认跳转行为 -->
|
||||
<a href="http://www.baidu.com" @click.prevent="linkClick">百度一下</a>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
methods: {
|
||||
linkClick: function () {
|
||||
console.log('超链接被点击了');
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上方代码中:
|
||||
|
||||
- 如果去掉`.prevent`,点击按钮后,既会打印log,又会跳转到百度页面。
|
||||
|
||||
- 现在加上了`.prevent`,就只会打印loeg,不会跳转到百度页面。
|
||||
|
||||
|
||||
|
||||
### `.prevent`的举例2
|
||||
|
||||
现在有一个form表单:
|
||||
|
||||
```html
|
||||
<form action="http://www.baidu.com">
|
||||
<input type="submit" value="表单提交">
|
||||
</form>
|
||||
```
|
||||
|
||||
|
||||
我们知道,上面这个表单因为`type="submit"`,因此它是一个提交按钮,点击按钮后,这个表单就会被提交到form标签的action属性中指定的那个页面中去。这是表单的默认行为。
|
||||
|
||||
现在,我们可以用`.prevent`来阻止这种默认行为。修改为:点击按钮后,不提交到服务器,而是执行我们自己想要的事件(在submit方法中另行定义)。如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 阻止表单中submit的默认事件 -->
|
||||
<form @submit.prevent action="http://www.baidu.com">
|
||||
<!-- 执行自定义的click事件 -->
|
||||
<input type="submit" @click="mySubmit" value="表单提交">
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
},
|
||||
methods: {
|
||||
mySubmit: function() {
|
||||
alert('ok');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上方代码中,我们通过`.prevent`阻止了提交按钮的默认事件,点击按钮后,执行的是`mySubmit()`方法里的内容。这个方法名是可以随便起的,我们甚至可以起名为`submit`,反正默认的submit已经失效了。
|
||||
|
||||
|
||||
|
||||
### `.self`举例
|
||||
|
||||
- `.self` 只有当事件在该元素本身(比如不是子元素)触发时,才会触发回调。
|
||||
|
||||
我们知道,在事件触发机制中,当点击子标签时,父标签会通过冒泡的形式被触发(父标签本身并没有被点击)。可如果我给父标签的点击事件设置`.self`修饰符,达到的效果是:子标签的点击事件不会再冒泡到父标签了,只有点击符标签本身,父标签的事件才会被触发。代码如下:
|
||||
|
||||
```html
|
||||
<div class="father" @click.self="fatherClick">
|
||||
```
|
||||
|
||||
|
||||
**疑问**:既然`.stop`和`.self`都可以阻止冒泡,那二者有什么区别呢?区别在于:前者能够阻止整个冒泡行为,而后者只能阻止自己身上的冒泡行为。
|
||||
|
||||
|
||||
835
12-Vue基础/01-03.Vue的系统指令(二).md
Normal file
835
12-Vue基础/01-03.Vue的系统指令(二).md
Normal file
@@ -0,0 +1,835 @@
|
||||
|
||||
|
||||
## 前言
|
||||
|
||||
文本主要内容:
|
||||
|
||||
- v-model
|
||||
|
||||
## v-model:双向数据绑定(只能用于表单元素)
|
||||
|
||||
之前的文章里,我们通过v-bind,给`<input>`标签绑定了`data`对象里的`name`属性。当`data`里的`name`的值发生改变时,`<input>`标签里的内容会自动更新。
|
||||
|
||||
可我现在要做的是:我在`<input>`标签里修改内容,要求`data`里的`name`的值自动更新。从而实现双向数据绑定。该怎么做呢?这就可以利用`v-model`这个属性。
|
||||
|
||||
**区别**:
|
||||
|
||||
- v-bind:只能实现数据的**单向**绑定,从 M 自动绑定到 V。
|
||||
|
||||
- v-model:只有`v-model`才能实现**双向**数据绑定。注意,v-model 后面不需要跟冒号,
|
||||
|
||||
**注意**:v-model 只能运用在**表单元素**中。常见的表单元素包括:input(radio, text, address, email....) 、select、checkbox 、textarea。
|
||||
|
||||
代码举例如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<form action="#">
|
||||
<!-- 将 input 标签中的value值双向绑定到 Vue实例中的data。注意,v-model 后面不需要跟冒号 -->
|
||||
<input type="text" id="username" v-model="myAccount.username">
|
||||
<input type="password" id="pwd" v-model="myAccount.userpwd">
|
||||
|
||||
<input type="submit" v-on:click="submit1" value="注册">
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
//上面的标签中采用v-model进行双向数据绑定,数据会自动更新到data里面来
|
||||
data: {
|
||||
name: 'smyhvae',
|
||||
myAccount: {username: '', userpwd: ''}
|
||||
},
|
||||
//在methods里绑定各种方法,根据业务需要进行操作
|
||||
methods: {
|
||||
submit1: function () {
|
||||
alert(this.myAccount.username + " pwd=" + this.myAccount.userpwd);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
此时,便可实现我们刚刚要求的双向数据绑定的效果。
|
||||
|
||||
## v-model举例:实现简易计算器
|
||||
|
||||
题目:现在两个输入框,用来做加减乘除,将运算的结果放在第三个输入框。
|
||||
|
||||
实现代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<input type="text" v-model="n1">
|
||||
|
||||
<select v-model="opt">
|
||||
<option value="+">+</option>
|
||||
<option value="-">-</option>
|
||||
<option value="*">*</option>
|
||||
<option value="/">/</option>
|
||||
</select>
|
||||
|
||||
<input type="text" v-model="n2">
|
||||
|
||||
<input type="button" value="=" @click="calc">
|
||||
|
||||
<input type="text" v-model="result">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
n1: 0,
|
||||
n2: 0,
|
||||
result: 0,
|
||||
opt: '+'
|
||||
},
|
||||
methods: {
|
||||
calc() { // 计算器算数的方法
|
||||
// 逻辑判断:
|
||||
switch (this.opt) {
|
||||
case '+':
|
||||
this.result = parseInt(this.n1) + parseInt(this.n2)
|
||||
break;
|
||||
case '-':
|
||||
this.result = parseInt(this.n1) - parseInt(this.n2)
|
||||
break;
|
||||
case '*':
|
||||
this.result = parseInt(this.n1) * parseInt(this.n2)
|
||||
break;
|
||||
case '/':
|
||||
this.result = parseInt(this.n1) / parseInt(this.n2)
|
||||
break;
|
||||
}
|
||||
|
||||
//上面的逻辑判断,可能有点啰嗦,我们还可以采取下面的这种方式进行逻辑判断
|
||||
// 注意:这是投机取巧的方式,正式开发中,尽量少用
|
||||
// var codeStr = 'parseInt(this.n1) ' + this.opt + ' parseInt(this.n2)'
|
||||
// this.result = eval(codeStr)
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
注意上方代码中的注释,可以了解下`eval()`的用法。
|
||||
|
||||
## Vue中通过属性绑定为元素设置class 类样式
|
||||
|
||||
注意,是**类样式**。
|
||||
|
||||
### 引入
|
||||
|
||||
我们先来看下面这段代码:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
.my-red {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.my-thin {
|
||||
/* 设置字体的粗细 */
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.my-italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.my-active {
|
||||
/* 设置字符之间的间距 */
|
||||
letter-spacing: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 class="my-red my-thin">我是生命壹号,smyhvae</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上面的代码中,我们直接通过正常的方式,给`<h1>`标签设置了两个 class 类的样式。代码抽取如下:
|
||||
|
||||
```html
|
||||
<h1 class="my-red my-thin">我是生命壹号,smyhvae</h1>
|
||||
```
|
||||
|
||||
上面的效果,我们还可以用Vue来写。这就引入了本段要讲的方式。
|
||||
|
||||
### 方式一:数组
|
||||
|
||||
**方式一**:直接传递一个数组。注意:这里的 class 需要使用 v-bind 做数据绑定。
|
||||
|
||||
代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<style>
|
||||
.my-red {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.my-thin {
|
||||
/* 设置字体的粗细 */
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.my-italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.my-active {
|
||||
/* 设置字符之间的间距 */
|
||||
letter-spacing: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- 普通写法 -->
|
||||
<h1 class="my-red my-thin">我是生命壹号,smyhvae</h1>
|
||||
|
||||
<!-- vue的写法1:数组的形式 -->
|
||||
<h1 :class="['my-red', 'my-thin']">我是smyhvae,生命壹号</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
代码抽取如下:
|
||||
|
||||
```html
|
||||
<!-- vue的写法1:数组的形式 -->
|
||||
<h1 :class="['my-red', 'my-thin']">我是smyhvae,生命壹号</h1>
|
||||
```
|
||||
|
||||
上方代码中,注意,数组里写的是字符串;如果不加单引号,就不是字符串了,而是变量。
|
||||
|
||||
演示效果如下:
|
||||
|
||||

|
||||
|
||||
### 写法二:在数组中使用三元表达式
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- vue的写法2:在数组中使用三元表达式。注意格式不要写错-->
|
||||
<!-- 通过data中布尔值 flag 来判断:如果 flag 为 true,就给 h1 标签添加`my-active`样式;否则,就不设置样式。 -->
|
||||
<h1 :class="[flag?'my-active':'']">我是smyhvae,生命壹号</h1>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
flag:true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
上方代码的意思是,通过data中布尔值 flag 来判断:如果 flag 为 true,就给 h1 标签添加`my-active`样式;否则,就不设置样式。
|
||||
|
||||
注意,三元表达式的格式不要写错了。
|
||||
|
||||
|
||||
### 写法三:在数组中使用 对象 来代替 三元表达式(提高代码的可读性)
|
||||
|
||||
上面的写法二,可读性较差。于是有了写法三。
|
||||
|
||||
**写法三**:在数组中使用**对象**来代替**三元表达式**。
|
||||
|
||||
代码如下:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- vue的写法3:在数组中使用对象来代替三元表达式。-->
|
||||
<h1 :class="[ {'my-active':flag} ]">我是smyhvae,生命壹号</h1>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
flag: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
### 写法四:直接使用对象
|
||||
|
||||
写法四:直接使用对象。代码如下:
|
||||
|
||||
```html
|
||||
<!-- vue的写法4:直接使用对象-->
|
||||
<!-- 在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名。由于 对象的属性名可带引号,也可不带引号,所以 这里我没写引号; 属性的值 是一个标识符 -->
|
||||
<h1 :class="{style1:true, style2:false}">我是smyhvae,生命壹号</h1>
|
||||
```
|
||||
|
||||
上方代码的意思是,给`<h1>`标签使用样式`style1`,不使用样式`style2`。注意:
|
||||
|
||||
1、既然class样式名是放在对象中的,这个样式名不能有中划线,比如说,写成`:class="{my-red:true, my-active:false}`,是会报错的。
|
||||
|
||||
2、我们也可以对象通过存放在 data 的变量中。也就是说,上方代码可以写成:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- vue的写法4:直接使用对象-->
|
||||
<!-- 在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名。由于 对象的属性名可带引号,也可不带引号,所以 这里我没写引号; 属性的值 是一个标识符 -->
|
||||
<h1 :class="classObj">我是smyhvae,生命壹号</h1>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
classObj:{style1:true, style2:false}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
## Vue中通过属性绑定为元素设置 style 行内样式
|
||||
|
||||
注意,是行内样式(即内联样式)。
|
||||
|
||||
### 写法一
|
||||
|
||||
**写法一**:直接在元素上通过 `:style` 的形式,书写样式对象。
|
||||
|
||||
例如:
|
||||
|
||||
```html
|
||||
<h1 :style="{color: 'red', 'font-size': '20px'}">我是生命壹号,smyhvae</h1>
|
||||
```
|
||||
|
||||
### 写法二
|
||||
|
||||
**写法二**:将样式对象,定义到 `data` 中,并直接引用到 `:style` 中。
|
||||
|
||||
也就是说,把写法一的代码改进一下。代码如下:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1 :style="styleObj">我是生命壹号,smyhvae</h1>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
styleObj: { color: 'red', 'font-size': '20px' }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
### 写法三
|
||||
|
||||
写法二只用到了一组样式。如果想定义**多组**样式,可以用写法三。
|
||||
|
||||
**写法三**:在 `:style` 中通过数组,引用多个 `data` 上的样式对象。
|
||||
|
||||
代码如下:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1 :style="[ styleObj1, styleObj2 ]">我是生命壹号,smyhvae</h1>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
styleObj1: { color: 'red', 'font-size': '20px' },
|
||||
styleObj2: { 'font-style': 'italic' }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
|
||||
## v-for:for循环的四种使用方式
|
||||
|
||||
**作用**:根据数组中的元素遍历指定模板内容生成内容。
|
||||
|
||||
### 引入
|
||||
|
||||
比如说,如果我想给一个`ul`中的多个`li`分别赋值1、2、3...。如果不用循环,就要挨个赋值:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<ul>
|
||||
<li>{{list[0]}}</li>
|
||||
<li>{{list[1]}}</li>
|
||||
<li>{{list[2]}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [1, 2, 3]
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
效果:
|
||||
|
||||

|
||||
|
||||
为了实现上面的效果,如果我用`v-for`进行赋值,代码就简洁很多了:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<ul>
|
||||
<!-- 使用v-for对多个li进行遍历赋值 -->
|
||||
<li v-for="item in list">{{item}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [1, 2, 3]
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
接下来,我们详细讲一下`v-for`的用法。需要声明的是,Vue 1.0的写法和Vue 2.0的写法是不一样的。本文全部采用Vue 2.0的写法。
|
||||
|
||||
### 方式一:普通数组的遍历
|
||||
|
||||
针对下面这样的数组:
|
||||
|
||||
```html
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
arr1: [2, 5, 3, 1, 1],
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
将数组中的**值**赋给li:
|
||||
|
||||
```html
|
||||
<li v-for="item in arr1">{{item}}</li>
|
||||
```
|
||||
|
||||
将数组中的**值和index**赋给li:
|
||||
|
||||
|
||||
```html
|
||||
<!-- 括号里如果写两个参数:第一个参数代表值,第二个参数代表index 索引 -->
|
||||
<li v-for="(item,index) in arr1">值:{{item}} --- 索引:{{index}}</li>
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
### 方式二:对象数组的遍历
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<ul>
|
||||
<!-- 对象数组的遍历。括号里如果写两个参数:第一个参数代表数组的单个item,第二个参数代表 index 索引-->
|
||||
<li v-for="(item, index) in dataList">姓名:{{item.name}} --- 年龄:{{item.age}} --- 索引:{{index}}</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
//对象数组
|
||||
dataList: [
|
||||
{ name: 'smyh', age: '26' },
|
||||
{ name: 'vae', age: '32' },
|
||||
{ name: 'xiaoming', age: '20' }
|
||||
]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
### 方式三:对象的遍历
|
||||
|
||||
针对下面这样的对象:
|
||||
|
||||
```html
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
obj1: {
|
||||
name: 'smyhvae',
|
||||
age: '26',
|
||||
gender: '男'
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
将上面的`obj1`对象的数据赋值给li,写法如下:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="app">
|
||||
<ul>
|
||||
<!-- 括号里如果写两个参数:则第一个参数代表value,第二个参数代表key -->
|
||||
<li v-for="(value,key) in obj1">值:{{value}} --- 键:{{key}} </li>
|
||||
|
||||
<h3>---分隔线---</h3>
|
||||
|
||||
<!-- 括号里如果写三个参数:则第一个参数代表value,第二个参数代表key,第三个参数代表index -->
|
||||
<li v-for="(value,key,index) in obj1">值:{{value}} --- 键:{{key}} --- index:{{index}} </li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
### 方式四:遍历数字
|
||||
|
||||
`in`后面还可以直接放数字。举例如下:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<!-- 对象数组的遍历 -->
|
||||
<!-- 注意:如果使用 v-for 遍历数字的话,前面的 myCount 值从 1 开始算起 -->
|
||||
<li v-for="myCount in 10">这是第 {{myCount}}次循环</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
### v-for中key的使用注意事项
|
||||
|
||||
|
||||
**注意**:在 Vue 2.2.0+ 版本里,当在**组件中**使用 v-for 时,key 属性是必须要加上的。
|
||||
|
||||
这样做是因为:每次 for 循环的时候,通过指定 key 来标示当前循环这一项的**唯一身份**。
|
||||
|
||||
> 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “**就地复用**” 策略。如果数据项的顺序被改变,Vue将**不是移动 DOM 元素来匹配数据项的顺序**, 而是**简单复用此处每个元素**,并且确保它在特定索引下显示已被渲染过的每个元素。
|
||||
|
||||
|
||||
> 为了给 Vue 一个提示,**以便它能跟踪每个节点的身份,从而重用和重新排序现有元素**,你需要为每项提供一个唯一 key 属性。
|
||||
|
||||
|
||||
key的类型只能是:string/number,而且要通过 v-bind 来指定。
|
||||
|
||||
|
||||
代码举例:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<div>
|
||||
<label>Id:
|
||||
<input type="text" v-model="id">
|
||||
</label>
|
||||
|
||||
<label>Name:
|
||||
<input type="text" v-model="name">
|
||||
</label>
|
||||
|
||||
<input type="button" value="添加" @click="add">
|
||||
</div>
|
||||
|
||||
<!-- 注意: v-for 循环的时候,key 属性只能使用 number获取string -->
|
||||
<!-- 注意: key 在使用的时候,必须使用 v-bind 属性绑定的形式,指定 key 的值 -->
|
||||
<!-- 在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果 v-for 有问题,必须 在使用 v-for 的同时,指定 唯一的 字符串/数字 类型 :key 值 -->
|
||||
<p v-for="item in list" :key="item.id">
|
||||
<input type="checkbox">{{item.id}} --- {{item.name}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
id: '',
|
||||
name: '',
|
||||
list: [
|
||||
{ id: 1, name: 'smyh' },
|
||||
{ id: 2, name: 'vae' },
|
||||
{ id: 3, name: 'smyhvae' },
|
||||
{ id: 4, name: 'xiaoming' },
|
||||
{ id: 5, name: 'xiaohong' }
|
||||
]
|
||||
},
|
||||
methods: {
|
||||
add() { // 添加方法
|
||||
this.list.unshift({ id: this.id, name: this.name })
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## v-if:设置元素的显示和隐藏(添加/删除DOM元素)
|
||||
|
||||
**作用**:根据表达式的值的真假条件,来决定是否渲染元素,如果为false则不渲染(达到隐藏元素的目的),如果为true则渲染。
|
||||
|
||||
在切换时,元素和它的数据绑定会被销毁并重建。
|
||||
|
||||
举例如下:(点击按钮时,切换和隐藏盒子)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<button v-on:click="toggle">显示/隐藏</button>
|
||||
<div v-if="isShow">我是盒子</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
isShow: true
|
||||
},
|
||||
methods: {
|
||||
toggle: function() {
|
||||
this.isShow = !this.isShow;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
## v-show:设置元素的显示和隐藏(在元素上添加/移除`style="display:none"`属性)
|
||||
|
||||
**作用**:根据表达式的真假条件,来切换元素的 display 属性。如果为false,则在元素上添加 `display:none`属性;否则移除`display:none`属性。
|
||||
|
||||
举例如下:(点击按钮时,切换和隐藏盒子)
|
||||
|
||||
我们直接把上一段代码中的`v-if`改成`v-show`就可以了:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<button v-on:click="toggle">显示/隐藏</button>
|
||||
<div v-show="isShow">我是盒子</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
isShow: true
|
||||
},
|
||||
methods: {
|
||||
toggle: function() {
|
||||
this.isShow = !this.isShow;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
### v-if和v-show的区别
|
||||
|
||||
`v-if`和`v-show`都能够实现对一个元素的隐藏和显示操作。
|
||||
|
||||
区别:
|
||||
|
||||
- v-if:每次都会重新添加/删除DOM元素
|
||||
|
||||
- v-show:每次不会重新进行DOM的添加/删除操作,只是在这个元素上添加/移除`style="display:none"`属性,表示节点的显示和隐藏。
|
||||
|
||||
优缺点:
|
||||
|
||||
- v-if:有较高的切换性能消耗。这个很好理解,毕竟每次都要进行dom的添加/删除操作。
|
||||
|
||||
- v-show:**有较高的初始渲染消耗**。也就是说,即使一开始`v-show="false"`,该节点也会被创建,只是隐藏起来了。而`v-if="false"`的节点,根本就不会被创建。
|
||||
|
||||
**总结**:
|
||||
|
||||
- 如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show
|
||||
|
||||
- 如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
607
12-Vue基础/01-04.Vue的举例:列表功能.md
Normal file
607
12-Vue基础/01-04.Vue的举例:列表功能.md
Normal file
@@ -0,0 +1,607 @@
|
||||
|
||||
## 列表功能举例
|
||||
|
||||
### 步骤 1:列表功能
|
||||
|
||||
完整的代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
.table {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
border-collapse: collapse; /*这一行,不能少:表格的两边框合并为一条*/
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
|
||||
}
|
||||
|
||||
.table tr td {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
|
||||
<table class="table">
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
<tr v-for="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<td><a href="#">删除</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [{id: 1, name: '奔驰', ctime: new Date}, {id: 2, name: '大众', ctime: new Date}]
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
**代码分析**:数据是存放在data的list中的,将data中的数据通过`v-for`遍历给表格。
|
||||
|
||||
|
||||
上方代码运行的效果:
|
||||
|
||||

|
||||
|
||||
### 步骤 2:无数据时,增加提示
|
||||
|
||||
如果list中没有数据,那么表格中就会只显示表头`<th>`,这样显然不太好看。
|
||||
|
||||
为此,我们需要增加一个`v-if`判断:当数据为空时,显示提示。如下:
|
||||
|
||||
```html
|
||||
<tr v-show="list.length == 0">
|
||||
<td colspan="4">列表无数据</td>
|
||||
</tr>
|
||||
```
|
||||
|
||||
代码解释:`colspan="4"`指的是让当前这个`<td>`横跨4个单元格的位置。如下:
|
||||
|
||||

|
||||
|
||||
### 步骤 3:item的添加
|
||||
|
||||
具体实现步骤如下:
|
||||
|
||||
(1)用户填写的数据单独存放在data属性里,并采用`v-model`进行双向绑定。
|
||||
|
||||
(2)用户把数据填好后,点击add按钮。此时需要增加一个点击事件的方法,将data中的数据放到list中(同时,清空文本框中的内容)。
|
||||
|
||||
(3)将数据展示出来。`v-for`有个特点:当list数组发生改变后,vue.js就会自动调用`v-for`重新将数据生成,这样的话,就实现了数据的自动刷新。
|
||||
|
||||
完整的代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
.table {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
border-collapse: collapse; /*这一行,不能少:表格的两边框合并为一条*/
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.table tr td {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.form button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
|
||||
<div class="form">
|
||||
|
||||
编号:<input type="text" v-model="formData.id">
|
||||
名称:<input type="text" v-model="formData.name">
|
||||
|
||||
<button v-on:click="addData">添加</button>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
<tr v-show="list.length == 0">
|
||||
<td colspan="4">列表无数据</td>
|
||||
</tr>
|
||||
<tr v-for="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<td><a href="#">删除</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [{id: 1, name: '奔驰', ctime: new Date}, {id: 2, name: '大众', ctime: new Date}],
|
||||
//用户添加的数据
|
||||
formData: {
|
||||
id: 0,
|
||||
name: ""
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
addData: function () {
|
||||
//将数据追加到list中
|
||||
var p = {id: this.formData.id, name: this.formData.name, ctime: new Date()};
|
||||
this.list.push(p);
|
||||
|
||||
//清空页面上的文本框中的数据
|
||||
this.formData.id = 0;
|
||||
this.formData.name = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
### 步骤 4:item的删除
|
||||
|
||||
html部分:
|
||||
|
||||
```html
|
||||
<!--绑定delete事件,根据括号里的参数进行删除-->
|
||||
<td><a href="#" v-on:click="delData(item.id)">删除</a></td>
|
||||
```
|
||||
|
||||
js部分:
|
||||
|
||||
```javascript
|
||||
delData: function (id) {
|
||||
// 0 提醒用户是否要删除数据
|
||||
if (!confirm('是否要删除数据?')) {
|
||||
//当用户点击的取消按钮的时候,应该阻断这个方法中的后面代码的继续执行
|
||||
return;
|
||||
}
|
||||
|
||||
// 1 调用list.findIndex()方法根据传入的id获取到这个要删除数据的索引值(在数组中的索引值)
|
||||
var index = this.list.findIndex(function (item) {
|
||||
return item.id == id
|
||||
});
|
||||
|
||||
// 2.0 调用方法:list.splice(待删除的索引, 删除的元素个数)
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
代码解释:`find()`和`findIndex()`是ES6中为数组新增的函数。详细解释如下:
|
||||
|
||||
```javascript
|
||||
// 根据id得到下标
|
||||
|
||||
// 默认去遍历list集合,将集合中的每个元素传入到function的item里,
|
||||
var index = this.list.findIndex(function(item){
|
||||
//根据item中的id属性去匹配传进来的id
|
||||
//如果是则返回true ;否返回false,继续下面的一条数据的遍历,以此类推
|
||||
return item.id ==id; //如果返回true,那么findIndex方法会将这个item对应的index
|
||||
});
|
||||
```
|
||||
|
||||
也就是说,我们是根据 item.id 找到这个 item 是属于list 数组中的哪个index索引。找到了index,就可以根据index来删除数组中的那个元素了。
|
||||
|
||||
当item被删除后,v-for会被自动调用,进而自动更新view。
|
||||
|
||||
完整版代码:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
.table {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
border-collapse: collapse; /*这一行,不能少:表格的两边框合并为一条*/
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.table tr td {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.form button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
|
||||
<div class="form">
|
||||
|
||||
编号:
|
||||
<input type="text" v-model="formData.id"> 名称:
|
||||
<input type="text" v-model="formData.name">
|
||||
|
||||
<button v-on:click="addData">添加</button>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
<tr v-show="list.length == 0">
|
||||
<td colspan="4">列表无数据</td>
|
||||
</tr>
|
||||
<tr v-for="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<!--绑定delete事件,根据括号里的参数进行删除-->
|
||||
<td>
|
||||
<a href="#" v-on:click="delData(item.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [{ id: 1, name: '奔驰', ctime: new Date }, { id: 2, name: '大众', ctime: new Date }],
|
||||
//用户添加的数据
|
||||
formData: {
|
||||
id: 0,
|
||||
name: ""
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
addData: function () {
|
||||
//将数据追加到list中
|
||||
var p = { id: this.formData.id, name: this.formData.name, ctime: new Date() };
|
||||
this.list.push(p);
|
||||
|
||||
//清空页面上的文本框中的数据
|
||||
this.formData.id = 0;
|
||||
this.formData.name = '';
|
||||
}, //注意:方法之间用逗号隔开,这个逗号不要忘记了
|
||||
|
||||
delData: function (id) {
|
||||
// 0 提醒用户是否要删除数据
|
||||
if (!confirm('是否要删除数据?')) {
|
||||
//当用户点击的取消按钮的时候,应该阻断这个方法中的后面代码的继续执行
|
||||
return;
|
||||
}
|
||||
|
||||
// 1 调用list.findIndex()方法根据传入的id获取到这个要删除数据的索引值
|
||||
var index = this.list.findIndex(function (item) {
|
||||
return item.id == id
|
||||
});
|
||||
|
||||
// 2 调用方法:list.splice(待删除的索引, 删除的元素个数)
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
### 步骤 5:按条件筛选item
|
||||
|
||||
现在要求实现的效果是,在搜索框输入关键字 keywords,列表中仅显示匹配出来的内容。也就是说:
|
||||
|
||||
- 之前, v-for 中的数据,都是直接从 data 上的list中直接渲染过来的。
|
||||
|
||||
- 现在, 我们在使用`v-for`进行遍历显示的时候,不能再遍历全部的 list 了;我们要自定义一个 search 方法,同时,把keywords作为参数,传递给 search 方法。即`v-for="item in search(keywords)"`。
|
||||
|
||||
在 search(keywords) 方法中,为了获取 list 数组中匹配的item,我们可以有两种方式实现。如下。
|
||||
|
||||
**方式一**:采用`forEach + indexOf()`
|
||||
|
||||
```javascript
|
||||
search(keywords) { // 根据关键字,进行数据的搜索,返回匹配的item
|
||||
|
||||
//实现方式一:通过 indexOf() 进行匹配。
|
||||
var newList = [];
|
||||
this.list.forEach(item => {
|
||||
if (item.name.indexOf(keywords) != -1) { //只要不等于 -1,就代表匹配到了
|
||||
newList.push(item)
|
||||
}
|
||||
})
|
||||
return newList
|
||||
}
|
||||
```
|
||||
|
||||
上方代码中, 我们要注意 indexOf(str) 的用法。举例如下:
|
||||
|
||||
```javascript
|
||||
var str = 'smyhvae';
|
||||
|
||||
console.log(str.indexOf('s')); //打印结果:0
|
||||
|
||||
console.log(str.indexOf('')); //打印结果:0。(说明,即使去匹配空字符串,也是返回0)
|
||||
|
||||
console.log(str.indexOf('h')); //打印结果:3
|
||||
|
||||
console.log(str.indexOf('x')); //打印结果:-1 (说明,匹配不到任何字符串)
|
||||
```
|
||||
|
||||
上方代码中,也就是说,如果参数为空字符串,那么,每个item都能匹配到。
|
||||
|
||||
**方式二**: filter + includes()方法
|
||||
|
||||
```javascript
|
||||
search(keywords) { // 根据关键字,进行数据的搜索,返回匹配的item
|
||||
|
||||
var newList = this.list.filter(item => {
|
||||
// 注意 : ES6中,为字符串提供了一个新方法,叫做 String.prototype.includes('要包含的字符串')
|
||||
// 如果包含,则返回 true ,否则返回 false
|
||||
if (item.name.includes(keywords)) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
||||
return newList
|
||||
}
|
||||
```
|
||||
|
||||
注意:forEach some filter findIndex,这些都属于数组的新方法,都会对数组中的每一项,进行遍历,执行相关的操作。这里我们采用数组中的 filter 方法,
|
||||
|
||||
总的来说,方式二的写法更优雅,因为字符串的 includes()方法确实很实用。
|
||||
|
||||
完整版代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
.table {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
border-collapse: collapse;/*这一行,不能少:表格的两边框合并为一条*/
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.table tr td {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.form button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
|
||||
<div class="form">
|
||||
|
||||
编号:
|
||||
<input type="text" v-model="formData.id"> 名称:
|
||||
<input type="text" v-model="formData.name">
|
||||
|
||||
<button v-on:click="addData">添加</button>
|
||||
搜索:
|
||||
<input type="text" v-model="keywords">
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
<tr v-show="list.length == 0">
|
||||
<td colspan="4">列表无数据</td>
|
||||
</tr>
|
||||
<tr v-for="item in search(keywords)">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<!--绑定delete事件,根据括号里的参数进行删除-->
|
||||
<td>
|
||||
<a href="#" v-on:click="delData(item.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [{ id: 1, name: '奔驰', ctime: new Date }, { id: 2, name: '大众', ctime: new Date }],
|
||||
//用户添加的数据
|
||||
formData: {
|
||||
id: '',
|
||||
name: ""
|
||||
},
|
||||
keywords: ""
|
||||
},
|
||||
|
||||
methods: {
|
||||
addData: function () {
|
||||
//将数据追加到list中
|
||||
var p = { id: this.formData.id, name: this.formData.name, ctime: new Date() };
|
||||
this.list.push(p);
|
||||
|
||||
//清空页面上的文本框中的数据
|
||||
this.formData.id = '';
|
||||
this.formData.name = '';
|
||||
}, //注意:方法之间用逗号隔开,这个逗号不要忘记了
|
||||
|
||||
delData: function (id) {
|
||||
// 0 提醒用户是否要删除数据
|
||||
if (!confirm('是否要删除数据?')) {
|
||||
//当用户点击的取消按钮的时候,应该阻断这个方法中的后面代码的继续执行
|
||||
return;
|
||||
}
|
||||
|
||||
// 1 调用list.findIndex()方法根据传入的id获取到这个要删除数据的索引值
|
||||
var index = this.list.findIndex(function (item) {
|
||||
return item.id == id
|
||||
});
|
||||
|
||||
// 2 调用方法:list.splice(待删除的索引, 删除的元素个数)
|
||||
this.list.splice(index, 1);
|
||||
},
|
||||
|
||||
search(keywords) { // 根据关键字,进行数据的搜索,返回匹配的item
|
||||
|
||||
var newList = this.list.filter(item => {
|
||||
// 注意 : ES6中,为字符串提供了一个新方法,叫做 String.prototype.includes('要包含的字符串')
|
||||
// 如果包含,则返回 true ,否则返回 false
|
||||
if (item.name.includes(keywords)) {
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
||||
return newList
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
备注:在1.x 版本中可以通过filterBy指令来实现过滤,但是在2.x中已经被废弃了。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
628
12-Vue基础/01-05.自定义过滤器:时间格式化举例.md
Normal file
628
12-Vue基础/01-05.自定义过滤器:时间格式化举例.md
Normal file
@@ -0,0 +1,628 @@
|
||||
|
||||
|
||||
|
||||
## 前言
|
||||
|
||||
|
||||
> 我们接着上一篇文章01-04来讲。
|
||||
|
||||
### 过滤器的概念
|
||||
|
||||
**概念**:Vue.js 允许我们自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache **插值表达式**、 **v-bind表达式**。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示。
|
||||
|
||||
|
||||
### Vue1.X中的系统过滤器
|
||||
|
||||
Vue提供了一系列的固定逻辑来使程序员更加容易的实现这些功能,这些过滤器称之为系统过滤器。
|
||||
|
||||
系统过滤器是Vue1.0中存在的,在Vue2.0中已经删除了。
|
||||
|
||||
系统过滤器的使用,可以参考参考文档:<http://v1-cn.vuejs.org/api/#过滤器>
|
||||
|
||||
|
||||
Vue也提供了一个接口用来供程序员定义属于自己的特殊逻辑,Vue称之为自定义过滤器。我们接下来讲一讲。
|
||||
|
||||
|
||||
## 自定义全局过滤器
|
||||
|
||||
文档地址:<http://v1-cn.vuejs.org/guide/custom-filter.html>
|
||||
|
||||
|
||||
### 全局过滤器的基本使用
|
||||
|
||||
|
||||
我们可以用全局方法`Vue.filter()`自定义一个全局过滤器。这样的话,每一个Vue的对象实例(每一个VM实例)都可以拿到这个过滤器。它接收两个参数:过滤器的名称 、过滤器函数。
|
||||
|
||||
比如说,我要将`曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人`这句 msg 中的“单纯”改为“邪恶”。可以这样做:
|
||||
|
||||
(1)在差值表达式中这样调用:
|
||||
|
||||
```html
|
||||
<p>{{ msg | msgFormat }</p>
|
||||
```
|
||||
|
||||
|
||||
上方代码的意思是说:
|
||||
|
||||
- **管道符前面**的`msg`:要把 `msg` 这段文本进行过滤,
|
||||
|
||||
- **管道符后面**的`msgFormat`:是通过`msgFormat`这个过滤器进行来操作。
|
||||
|
||||
(2)定义过滤器`msgFormat`:
|
||||
|
||||
```javascript
|
||||
// Vue.filter 中的第一个参数是过滤器的名称,第二个参数是具体的过滤器函数
|
||||
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat
|
||||
Vue.filter('msgFormat', function (myMsg) { // function 的第一个参数指的是管道符前面的 msg
|
||||
// 字符串的 replace 方法,第一个参数,除了可写一个 字符串之外,还可以定义一个正则
|
||||
return myMsg.replace(/单纯/g, '邪恶')
|
||||
})
|
||||
```
|
||||
|
||||
上方代码解释:
|
||||
|
||||
- `Vue.filter(‘过滤器的名称’, 具体的过滤器函数)`中的第一个参数指的就是过滤器的名称(必须和**管道符后面**的名称**完全一致**),第二个参数是具体的过滤器函数
|
||||
|
||||
- 过滤器函数function中,第一个参数指的**管道符前面的**msg。
|
||||
|
||||
- `replace()`方法是用来做字符串的替换的。第一个参数如果只写成`单纯`,那么就会只修改 msg 中的第一个`单纯`字样。所以这里就用正则去匹配msg 中所有的`单纯`字样。
|
||||
|
||||
最终,完整版代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 通过 过滤器 msgFormat 对 msg 进行过滤-->
|
||||
<p>{{ msg | msgFormat }}</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat
|
||||
Vue.filter('msgFormat', function (myMsg) {
|
||||
// 字符串的 replace 方法,第一个参数,除了可写一个 字符串之外,还可以定义一个正则
|
||||
//将 myMsg 中的所有`单纯`字样,修改为`邪恶`
|
||||
return myMsg.replace(/单纯/g, '邪恶')
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
网页显示效果如下:
|
||||
|
||||

|
||||
|
||||
### 给过滤器添加多个参数
|
||||
|
||||
上面的举例代码中,`{{ msg | msgFormat }}`中,**过滤器的调用并没有加参数**,其实它还可以添加多个参数。
|
||||
|
||||
接下来,我们在上面的举例代码中进行改进。
|
||||
|
||||
|
||||
**改进一**:过滤器加一个参数。如下:
|
||||
|
||||
|
||||
将 msg 这个字符串中的“单纯”改为 xxx 变量。代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 【重要】通过 过滤器 msgFormat 对 msg 进行过滤。括号里的参数代表 function中的 arg2-->
|
||||
<p>{{ msg | msgFormat('xxx') }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat
|
||||
Vue.filter('msgFormat', function (myMsg, arg2) {
|
||||
// 字符串的 replace 方法:第一个参数,除了可写一个 字符串之外,还可以定义一个正则;第二个参数代表要替换为上面的xxx
|
||||
//将 myMsg 中的所有`单纯`字样,修改为 arg2
|
||||
return myMsg.replace(/单纯/g, arg2)
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
||||
注意代码中那行重要的注释:括号里的参数代表 function中的 arg2。
|
||||
|
||||
**改进二**:过滤器加两个参数。如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 通过 过滤器 msgFormat 对 msg 进行过滤-->
|
||||
<!-- 【重要】括号里的第一个参数代表 function 中的 arg2,括号里的第二个参数代表 function 中的 arg3-->
|
||||
<p>{{ msg | msgFormat('【牛x】', '【参数arg3】') }}</p>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat
|
||||
Vue.filter('msgFormat', function (myMsg, arg2, arg3) {
|
||||
// 字符串的 replace 方法:第一个参数,除了可写一个 字符串之外,还可以定义一个正则;第二个参数代表要替换为 xxx
|
||||
//将 myMsg 中的所有`单纯`字样,修改为`arg2 + arg3`
|
||||
return myMsg.replace(/单纯/g, arg2 + arg3)
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
**改进3:同时使用多个过滤器**
|
||||
|
||||
对 msg 同时使用多个过滤器。例如:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 通过 两个过滤器(msgFormat、myFilter2)对 msg 进行过滤-->
|
||||
<!-- 将 msg 交给第一个过滤器来处理,然后将处理的结果交给第二个过滤器来处理-->
|
||||
<p>{{ msg | msgFormat('【牛x】', '【参数arg3】') | myFilter2}}</p>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat
|
||||
Vue.filter('msgFormat', function (myMsg, arg2, arg3) {
|
||||
// 字符串的 replace 方法:第一个参数,除了可写一个 字符串之外,还可以定义一个正则;第二个参数代表要替换为 xxx
|
||||
//将 myMsg 中的所有`单纯`字样,修改为`arg2 + arg3`
|
||||
return myMsg.replace(/单纯/g, arg2 + arg3)
|
||||
})
|
||||
|
||||
//定义第二个全局过滤器
|
||||
Vue.filter('myFilter2', function (myMsg) {
|
||||
//在字符串 msg 的最后面加上【后缀】
|
||||
return myMsg + '【后缀】'
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
上方代码中,添加了多个过滤器,实现的思路是:**将 msg 交给第一个过滤器来处理,然后将处理的结果交给第二个过滤器来处理** 。
|
||||
|
||||
|
||||
|
||||
|
||||
### 举例1:时间格式化
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
{{ time }}
|
||||
<br /> {{ time | datefmt }}
|
||||
</div>
|
||||
|
||||
<div id="app1">
|
||||
{{ time | datefmt }}
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
// 定义一个名称为 datafmt的全局过滤器
|
||||
Vue.filter('datefmt', function (input) {
|
||||
// 过滤器的逻辑:将input的值格式化成 yyyy-MM-dd 字符串输出
|
||||
var res = '';
|
||||
var year = input.getFullYear();
|
||||
var month = input.getMonth() + 1;
|
||||
var day = input.getDate();
|
||||
|
||||
res = year + '-' + month + '-' + day;
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app1',
|
||||
data: {
|
||||
time: new Date()
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
time: new Date()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行效果:
|
||||
|
||||

|
||||
|
||||
### 举例2:时间格式化
|
||||
|
||||
上面的举例1,时间格式化的过滤器,我们还有个更高端的写法:(字符串模板)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
2018-05-25T14:06:51.618Z
|
||||
<br /> {{ '2018-05-25T14:06:51.618Z' | dateFormat }}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
Vue.filter('dateFormat', function (dateStr, pattern = "") {
|
||||
// 根据给定的时间字符串,得到特定的时间
|
||||
var dt = new Date(dateStr)
|
||||
|
||||
// yyyy-mm-dd
|
||||
var y = dt.getFullYear()
|
||||
var m = dt.getMonth() + 1
|
||||
var d = dt.getDate()
|
||||
|
||||
// return y + '-' + m + '-' + d
|
||||
|
||||
if (pattern.toLowerCase() === 'yyyy-mm-dd') { //如果调用过滤器的参数写的是 yyyy-mm-dd,那就按照这种 yyyy-mm-dd 的格式写
|
||||
//这里用的是字符串模板
|
||||
return `${y}-${m}-${d}`
|
||||
} else { //否则(比如说调用过滤器时不写参数),后面就补上 时-分-秒
|
||||
var hh = dt.getHours()
|
||||
var mm = dt.getMinutes()
|
||||
var ss = dt.getSeconds()
|
||||
|
||||
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
time: new Date()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||

|
||||
|
||||
【荐】**举例2的改进**:(字符串的padStart方法使用)
|
||||
|
||||
上图中,我们可以看到,箭头处的时间有些问题,比如说,`6`要写成`06`更合适。为了实现这个功能,我们可以这样做:
|
||||
|
||||
使用ES6中的字符串新方法 `String.prototype.padStart(maxLength, fillString='')` 或 `String.prototype.padEnd(maxLength, fillString='')`来填充字符串。 `pad`在英文中指的是`补充`。
|
||||
|
||||
实现举例如下:
|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
2018-05-25T14:06:51.618Z
|
||||
<br /> {{ '2018-05-25T14:06:51.618Z' | dateFormat }}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
Vue.filter('dateFormat', function (dateStr, pattern) {
|
||||
// 根据给定的时间字符串,得到特定的时间
|
||||
var dt = new Date(dateStr)
|
||||
|
||||
// yyyy-mm-dd
|
||||
var y = dt.getFullYear()
|
||||
var m = (dt.getMonth() + 1).toString().padStart(2, '0')
|
||||
var d = dt.getDate().toString().padStart(2, '0')
|
||||
|
||||
if (pattern && pattern.toLowerCase() === 'yyyy-mm-dd') { //如果调用过滤器的参数写的是 yyyy-mm-dd,那就按照这种 yyyy-mm-dd 的格式写
|
||||
//这里用的是字符串模板
|
||||
return `${y}-${m}-${d}`
|
||||
} else { //否则(比如说调用过滤器时不写参数),后面就补上 时-分-秒
|
||||
var hh = dt.getHours().toString().padStart(2, '0')
|
||||
var mm = dt.getMinutes().toString().padStart(2, '0')
|
||||
var ss = dt.getSeconds().toString().padStart(2, '0')
|
||||
|
||||
return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~`
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
time: new Date()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
运行效果如下:
|
||||
|
||||

|
||||
|
||||
`pattern`参数的解释:
|
||||
|
||||
在做`if (pattern && pattern.toLowerCase() === 'yyyy-mm-dd')`这个判断时,逻辑是:**先保证pattern参数传进来了,然后继续后面的判断**。
|
||||
|
||||
我们不能写成:`if (pattern.toLowerCase() === 'yyyy-mm-dd')`。因为,万一在调用的时候,不传递参数pattern,那么 if语句就相当于`if (undefined.toLowerCase() === 'yyyy-mm-dd')`,就会报错。
|
||||
|
||||
当然,ES6中有个新特性叫“默认参数”,我们就可以这样写:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
2018-05-25T14:06:51.618Z
|
||||
<br /> {{ '2018-05-25T14:06:51.618Z' | dateFormat }}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
Vue.filter('dateFormat', function (dateStr, pattern = '') {
|
||||
// 根据给定的时间字符串,得到特定的时间
|
||||
var dt = new Date(dateStr)
|
||||
|
||||
// yyyy-mm-dd
|
||||
var y = dt.getFullYear()
|
||||
var m = (dt.getMonth() + 1).toString().padStart(2, '0')
|
||||
var d = dt.getDate().toString().padStart(2, '0')
|
||||
|
||||
if (pattern.toLowerCase() === 'yyyy-mm-dd') { //如果调用过滤器的参数写的是 yyyy-mm-dd,那就按照这种 yyyy-mm-dd 的格式写
|
||||
//这里用的是字符串模板
|
||||
return `${y}-${m}-${d}`
|
||||
} else { //否则(比如说调用过滤器时不写参数),后面就补上 时-分-秒
|
||||
var hh = dt.getHours().toString().padStart(2, '0')
|
||||
var mm = dt.getMinutes().toString().padStart(2, '0')
|
||||
var ss = dt.getSeconds().toString().padStart(2, '0')
|
||||
|
||||
return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~`
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
time: new Date()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 自定义私有过滤器
|
||||
|
||||
**私有过滤器**:在某一个 vue 对象内部定义的过滤器称之为私有过滤器。这种过滤器只有在当前vue对象的el指定的监管区域有用。
|
||||
|
||||
|
||||
**举例**:日期格式化
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
{{ time }}
|
||||
<br />
|
||||
{{ time | datefmt }}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
time: new Date()
|
||||
},
|
||||
//在某一个vue对象内部定义的过滤器称之为私有过滤器,
|
||||
//这种过滤器只有在当前vue对象el指定的监管的区域有用
|
||||
filters: {
|
||||
// input是自定义过滤器的默认参数,input的值永远都是取自于 | 左边的内容
|
||||
datefmt: function (input) {
|
||||
// 定义过滤器的内容:将input的值格式化成 yyyy-MM-dd 字符串输出
|
||||
var res = '';
|
||||
var year = input.getFullYear();
|
||||
var month = input.getMonth() + 1;
|
||||
var day = input.getDate();
|
||||
|
||||
res = year + '-' + month + '-' + day;
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上面的代码中,我们在vue实例中,通过`filters`关键字,在里面定义了一个局部过滤器`datefmt`。
|
||||
|
||||
|
||||
运行结果:
|
||||
|
||||

|
||||
|
||||
第一行代码显示的是默认的date。第二行代码显示的是格式化之后的date,说明过滤器是起到了作用的。
|
||||
|
||||
### 总结
|
||||
|
||||
过滤器调用的时候,采用的是**就近原则**,如果私有过滤器和全局过滤器名称一致了,这时候 优先调用私有过滤器。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## axios
|
||||
|
||||
axios是在Vue中专门用来发送ajax请求的。
|
||||
|
||||
但是,axios并不依赖于Vue.js库,而是基于promise的。
|
||||
375
12-Vue基础/01-06.自定义按键修饰符&自定义指令.md
Normal file
375
12-Vue基础/01-06.自定义按键修饰符&自定义指令.md
Normal file
@@ -0,0 +1,375 @@
|
||||
|
||||
|
||||
## v-on的按键修饰符
|
||||
|
||||
### Vue 内置的按键修饰符
|
||||
|
||||
通俗一点讲,指的是:监听键盘输入的事件。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符。如下:
|
||||
|
||||
Vue内置的按键修饰符:
|
||||
|
||||
```
|
||||
.enter
|
||||
.tab
|
||||
.delete (捕获 “删除” 和 “退格” 键)
|
||||
.esc
|
||||
.space
|
||||
.up
|
||||
.down
|
||||
.left
|
||||
.right
|
||||
1.0.8+版本:支持单字母的按键别名。
|
||||
```
|
||||
|
||||
|
||||
比如说,`keyup`指的是:键盘(任何键位)抬起时的监听事件。`.enter`指的是:按enter键的按键修饰符。我们把这两个结合起来看看。
|
||||
|
||||
**`@keyup.enter`举例**:按enter键后的监听事件
|
||||
|
||||
`@keyup.enter="addData"`表示:按住enter键后,执行addData()方法。**全称**是`v-on:key.enter="addData"`。
|
||||
|
||||
我们还是拿`01-04`这篇文章中的列表功能来举例。之前是点击“添加”按钮后,列表中会添加一个item。现在要求:在**输入框**中按enter键后,也能添加一个item。
|
||||
|
||||
核心代码如下:
|
||||
|
||||
```html
|
||||
<input type="text" v-model="formData.name" @keyup.enter="addData">
|
||||
```
|
||||
|
||||
注意,如果写成`@keyup="addData"`,效果却是:只要键盘的任何键位打了字(还没来得及按enter键),就会执行addData()方法,这种效果显然不是我们想要的。所以要加上修饰符`.enter`,表示只针对enter键。
|
||||
|
||||
### 自定义的按键修饰符
|
||||
|
||||
如果我们直接在代码的`<input>`标签里写`@keyup.f2="addData"`,那么,按住「F2键」后,是没有效果的,因为「F2键」不是内置的按键修饰符(如果F2不能验证,你可以试一下F7)。
|
||||
|
||||
我们知道,每个按键都有一个键盘码。参考链接:
|
||||
|
||||
- [js 里面的键盘事件对应的键码](http://www.cnblogs.com/wuhua1/p/6686237.html)
|
||||
|
||||
通过查阅,我们知道了「F2键」的键盘码为`113`,那代码可以这样写:(按住F2键后,执行 addData 方法)
|
||||
|
||||
```html
|
||||
<input type="text" v-model="formData.name" @keyup.113="addData">
|
||||
```
|
||||
|
||||
虽然键盘码很全,但是不好记呀。于是,接下来,我们给键盘码定义别名。
|
||||
|
||||
**自定义全局按键修饰符**:
|
||||
|
||||
```
|
||||
//自定义全局按键修饰符
|
||||
Vue.config.keyCodes.f2 = 113;
|
||||
```
|
||||
|
||||
上方代码的书写位置,与自定义全局过滤器的位置,是并列的。
|
||||
|
||||
然后,我们就可以使用键盘码的别名了。
|
||||
|
||||
## 自定义全局指令
|
||||
|
||||
### 自定义全局指令的举例1
|
||||
|
||||
**举例1**:让指定文本框自动获取焦点
|
||||
|
||||
如果我们想实现这个例子,原生js的写法是:
|
||||
|
||||
```javascript
|
||||
//原生js写法:网页一打开,就让指定的输入框自动获取焦点
|
||||
document.getElementById('search').focus()
|
||||
```
|
||||
|
||||
代码的位置:
|
||||
|
||||

|
||||
|
||||
但我们不建议这样做。我们可以通过Vue中的自定义指令来实现这个例子。步骤如下。
|
||||
|
||||
(1)使用`Vue.directive()`自定义全局指令:
|
||||
|
||||
```javascript
|
||||
|
||||
//自定义全局指令 v-focus:让文本框自动获取焦点
|
||||
//参数1:指令的名称。注意,在定义的时候,指令的名称前面,不需要加 v- 前缀;但是:在`调用`的时候,必须在指令名称前 加上 v- 前缀
|
||||
//参数2:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
|
||||
Vue.directive('focus', {
|
||||
//在每个函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象(DOM对象)
|
||||
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,【只执行一次】
|
||||
// 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
|
||||
// 因为,一个元素,只有插入DOM之后,才能获取焦点
|
||||
// el.focus()
|
||||
},
|
||||
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
|
||||
el.focus()
|
||||
// 和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
|
||||
},
|
||||
updated: function (el) { // 当VNode更新的时候,会执行 updated, 【可能会触发多次】
|
||||
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
上方的代码中,如果我们把`el.focus()`这行代码写在`bind`方法里,是没有效果的(但不会报错)。没有效果是因为,在执行到`bind`方法的时候,元素还没有插入到dom中去。
|
||||
|
||||
由此可以看看出:`bind`、`inserted`、`updated`这三个钩子函数的执行时机不同,且执行的次数有区别。
|
||||
|
||||
(2)在指定的文本框上加``:
|
||||
|
||||
```html
|
||||
<input type="text" id="search" v-model="keywords" v-focus>
|
||||
```
|
||||
|
||||
完整版代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
搜索框:
|
||||
<input type="text" id="search" v-model="name" v-focus>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//自定义全局指令 v-focus,让文本框自动获取焦点
|
||||
//参数1:指令的名称。注意,在定义的时候,指令的名称前面,不需要加 v- 前缀;但是:在`调用`的时候,必须在指令名称前 加上 v- 前缀
|
||||
//参数2:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
|
||||
Vue.directive('focus', {
|
||||
//在每个函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象(DOM对象)
|
||||
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,【只执行一次】
|
||||
// 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
|
||||
// 因为,一个元素,只有插入DOM之后,才能获取焦点
|
||||
// el.focus()
|
||||
},
|
||||
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
|
||||
el.focus()
|
||||
// 和JS行为有关的操作,最好在 inserted 中去执行,防止 JS行为不生效
|
||||
},
|
||||
updated: function (el) { // 当VNode更新的时候,会执行 updated, 【可能会触发多次】
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: 'smyhvae'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 自定义全局指令:使用钩子函数的第二个binding参数拿到传递的值
|
||||
|
||||
**举例2**:设置DOM元素的color样式
|
||||
|
||||
|
||||
参考举例1中的写法,我们可能会这样给DOM元素设置样式:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
搜索框:
|
||||
<input type="text" id="search" v-model="name" v-color>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//自定义全局指令 v-color:设置DOM元素的color属性
|
||||
Vue.directive('color', {
|
||||
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,【只执行一次】
|
||||
el.style.color = 'red';
|
||||
},
|
||||
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
|
||||
// 和JS行为有关的操作,最好在 inserted 中去执行,防止 JS行为不生效
|
||||
//el.focus()
|
||||
},
|
||||
updated: function (el) { // 当VNode更新的时候,会执行 updated, 【可能会触发多次】
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
如上方代码所示,我们自定义了一个指令`v-color`,然后在`input`标签中用上了这个指令,就给元素设置了color属性。但是这个代码有个弊端是:color的属性值在定义指令的时候,被写死了。如何完善呢?我们可以在DOM元素中传参。一起来看看。
|
||||
|
||||
代码如下:【荐】
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
搜索框1:
|
||||
<input type="text" id="search" v-model="name" v-color="'green'">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//自定义全局指令 v-color:设置DOM元素的color属性
|
||||
Vue.directive('color', {
|
||||
// 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
|
||||
// 将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
|
||||
// 意思是说,我们可以把样式的代码写到bind中去(即使这个时候,dom元素还没有被创建)
|
||||
bind: function (el, binding) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,【只执行一次】
|
||||
|
||||
console.log(binding.name); //打印结果:color
|
||||
console.log(binding.value); //打印结果:green
|
||||
console.log(binding.expression); //'green'
|
||||
|
||||
el.style.color = binding.value// 通过bining拿到v-color中传递过来的值
|
||||
|
||||
},
|
||||
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
|
||||
// 和JS行为有关的操作,最好在 inserted 中去执行,防止 JS行为不生效
|
||||
//el.focus()
|
||||
},
|
||||
updated: function (el) { // 当VNode更新的时候,会执行 updated, 【可能会触发多次】
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: 'smyhvae'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上方代码中,bind方法里传递的第二个参数`binding`,可以拿到DOM元素中`v-color`里填的值。注意,`v-color="'green'"`,这里面写的是字符串常量;如果去掉单引号,就成了变量,不是我们想要的。
|
||||
|
||||
效果:
|
||||
|
||||

|
||||
|
||||
**自定义全局指令的简写形式**:
|
||||
|
||||
|
||||
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如上面的代码中,我们可以写成简写形式:
|
||||
|
||||
```javascript
|
||||
Vue.directive('color', function (el, binding) { //注意,这个function等同于把代码写到了 bind 和 update 中去
|
||||
el.style.color = binding.value
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## 自定义私有指令
|
||||
|
||||
**自定义私有指令**:在某一个 vue 对象内部自定义的指令称之为私有指令。这种指令只有在当前vue对象的el指定的监管区域有用。
|
||||
|
||||
代码举例:(设置文字的font-weight属性)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<span v-fontweight="600">生命壹号</span>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: 'smyhvae'
|
||||
},
|
||||
//自定义私有指令
|
||||
directives: {
|
||||
'fontweight': {
|
||||
bind: function (el, binding) {
|
||||
el.style.fontWeight = binding.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
效果:
|
||||
|
||||

|
||||
|
||||
注意, el.style.fontWeight设置属性值,至少要600,否则看不到加粗的效果。
|
||||
|
||||
**自定义私有指令的简写形式**:
|
||||
|
||||
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如上面的代码中,我们可以写成简写形式:
|
||||
|
||||
```
|
||||
//自定义私有指令(简写形式)
|
||||
directives: {
|
||||
'fontweight': function (el, binding) { //注意,这个function等同于把代码写到了 bind 和 update 中去
|
||||
el.style.fontWeight = binding.value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
192
12-Vue基础/02-Vue实例的生命周期函数.md
Normal file
192
12-Vue基础/02-Vue实例的生命周期函数.md
Normal file
@@ -0,0 +1,192 @@
|
||||
|
||||
|
||||
|
||||
## 介绍
|
||||
|
||||
|
||||
|
||||
- [vue实例的生命周期](https://cn.vuejs.org/v2/guide/instance.html#实例生命周期):从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期。
|
||||
|
||||
|
||||
- [生命周期钩子](https://cn.vuejs.org/v2/api/#选项-生命周期钩子):就是生命周期事件的别名而已。
|
||||
|
||||
生命周期钩子 = 生命周期函数 = 生命周期事件。
|
||||
|
||||
|
||||
## 生命周期函数的主要分类
|
||||
|
||||

|
||||
|
||||
根据上面这张图,我们把生命周期函数主要分为三类。
|
||||
|
||||
|
||||
### 1、创建期间的生命周期函数
|
||||
|
||||
- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
|
||||
|
||||
- created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板。我们可以在这里进行Ajax请求。
|
||||
|
||||
- beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
|
||||
|
||||
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示。(mounted之后,表示**真实DOM渲染完了,可以操作DOM了**)
|
||||
|
||||
|
||||
**举例**:
|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--这个div区域就是MVVM中的 View-->
|
||||
<div id="app">
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: 'hello vuejs'
|
||||
},
|
||||
// 这是第1个生命周期函数,表示实例完全被创建出来之前,会执行它
|
||||
beforeCreate: function () {
|
||||
console.log('01 beforeCreate', this.msg);
|
||||
//注意:在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
|
||||
},
|
||||
|
||||
// 这是第2个生命周期函数
|
||||
created: function () {
|
||||
console.log('02 created', this.msg);
|
||||
//注意:如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
|
||||
},
|
||||
|
||||
// 这是第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把模板渲染到页面中
|
||||
beforeMount: function () {
|
||||
console.log('03 beforeMount', this.msg);
|
||||
// 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
|
||||
},
|
||||
|
||||
// 这是第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
|
||||
mounted: function () {
|
||||
console.log('04 mounted', this.msg);
|
||||
// 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了
|
||||
// 此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
打印结果:
|
||||
|
||||

|
||||
|
||||
### 运行期间的生命周期函数
|
||||
|
||||
- beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
|
||||
|
||||
- updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了。
|
||||
|
||||
PS:数据发生变化时,会触发这两个方法。不过,我们一般用watch来做。
|
||||
|
||||
|
||||
**举例**:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--这个div区域就是MVVM中的 View-->
|
||||
<div id="app">
|
||||
<input type="button" value="修改flag" @click="myMethod">
|
||||
<h3 id="h3">{{ flag }}</h3>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: 'hello vue',
|
||||
flag: false
|
||||
},
|
||||
|
||||
methods: {
|
||||
myMethod: function () {
|
||||
this.flag = true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 接下来的是运行中的两个事件
|
||||
// 这时候,我们的界面还没有被更新【但是,数据被更新了吗? 数据肯定被更新了】
|
||||
beforeUpdate() {
|
||||
console.log('-------------05 beforeUpdate', this.msg);
|
||||
|
||||
// 结论:当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
|
||||
console.log('界面上DOM元素显示的内容:' + document.getElementById('h3').innerText)
|
||||
console.log('data 中的 msg 数据:' + this.flag)
|
||||
},
|
||||
updated() {
|
||||
console.log('-------------06 updated', this.msg);
|
||||
|
||||
// 结论:updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
|
||||
console.log('界面上DOM元素显示的内容:' + document.getElementById('h3').innerText)
|
||||
console.log('data 中的 msg 数据:' + this.flag)
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
当我们点击按钮后,运行效果是:
|
||||
|
||||

|
||||
|
||||
可以看出:
|
||||
|
||||
- 当执行 beforeUpdate 的时候,页面中的显示的数据还是旧的,但此时 data 数据是最新的
|
||||
|
||||
- updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
|
||||
|
||||
|
||||
|
||||
### 3、销毁期间的生命周期函数
|
||||
|
||||
- beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
|
||||
|
||||
- destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
|
||||
|
||||
PS:可以在beforeDestory里**清除定时器、或清除事件绑定**。
|
||||
|
||||
|
||||
## 生命周期函数图解
|
||||
|
||||

|
||||
|
||||
PS:图片来自网络。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
551
12-Vue基础/03-Vue中的Ajax请求.md
Normal file
551
12-Vue基础/03-Vue中的Ajax请求.md
Normal file
@@ -0,0 +1,551 @@
|
||||
|
||||
|
||||
|
||||
## vue-resource的介绍
|
||||
|
||||
`vue-resource`是Vue高度集成的第三方包。
|
||||
|
||||
官网链接:
|
||||
|
||||
- 文档(http相关):<https://github.com/pagekit/vue-resource/blob/master/docs/http.md>
|
||||
|
||||
vue-resource 依赖于 Vue。所以,我们要按照先后顺序,导入vue.js和vue-resource.js文件。
|
||||
|
||||
**解释**:
|
||||
|
||||
`vue.js`文件向Windows对象暴露了`Vue`这个关键词;`vue-resource.js`向Vue身上挂载了`this.$http`这个属性。于是,我们可以直接写`this.$http.get`或者`this.$http.post`或者`this.$http.jsonp`来调用。
|
||||
|
||||
## vue-resource 发送Ajax请求
|
||||
|
||||
常见的数据请求类型包括:get、post、jsonp。下面我们分别讲一讲。
|
||||
|
||||
|
||||
### get 请求
|
||||
|
||||
**格式举例**:
|
||||
|
||||
```javascript
|
||||
this.$http.get(url)
|
||||
.then(function (result) { // 当发起get请求之后,通过 .then 来设置成功的回调函数
|
||||
console.log(result.body); // response.body就是服务器返回的成功的数据
|
||||
var result = result.body;
|
||||
},
|
||||
function (err) {
|
||||
//err是异常数据
|
||||
});
|
||||
```
|
||||
|
||||
获取到的`response.body`就是要获取的数据,但直接打印出来是 object,所以要记得转成string。
|
||||
|
||||
**举例**:获取数据
|
||||
|
||||
现规定,获取品牌数据的 api 接口说明如下:
|
||||
|
||||

|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
#app {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#tb {
|
||||
width: 800px;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#tb th {
|
||||
background-color: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#tb td {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
<script src="../vue.js"></script>
|
||||
<script src="../vue-resource121.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<input type="text" v-model="id">
|
||||
<input type="text" v-model="pname">
|
||||
<button>添加数据</button>
|
||||
|
||||
<table id="tb">
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el :'#app',
|
||||
data:{
|
||||
list:[]
|
||||
},
|
||||
// Vue对象实例创建成功以后就会自动调用这个方法
|
||||
created:function(){
|
||||
this.getlist();
|
||||
},
|
||||
methods:{
|
||||
getlist:function(){
|
||||
// 请求服务器的api获取到品牌的数据列表
|
||||
this.$http.get('http://vueapi.ittun.com/api/getprodlist')
|
||||
.then(function(response){
|
||||
// 1、处理服务器异常信息提示
|
||||
if(response.body.status != 0){
|
||||
alert(response.body.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2、处理正常的数据逻辑
|
||||
this.list = response.body.message; //直接将数据放到list数组当中,页面就会自动显示
|
||||
console.log(this.list);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
上方代码中,我们用到了生命周期函数`created`,意思是:程序一加载,就马上在`created`这个函数里执行`getlist()`方法。
|
||||
|
||||
|
||||
运行的结果如下:
|
||||
|
||||

|
||||
|
||||
如果我直接在浏览器中输入请求的url,获取的json数据如下:(可以看到,这种方式获取的是相同的数据)
|
||||
|
||||

|
||||
|
||||
|
||||
### post请求
|
||||
|
||||
**格式举例**:
|
||||
|
||||
```javascript
|
||||
// 方法:$http.post(url, 传给服务器的请求体中的数据, {emulateJSON:true})
|
||||
// 通过 post 方法的第三个参数{ emulateJSON: true } ,来设置 提交的内容类型 为 普通表单数据格式
|
||||
this.$http.post(url, { name: '奔驰' }, { emulateJSON: true })
|
||||
.then(function (response) {
|
||||
alert(response.body.message);
|
||||
},
|
||||
function (error) {
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
上方代码中,post()方法中有三个参数,其中第三个参数是固定值,照着写就可以了。
|
||||
|
||||
|
||||
**代码举例**:(添加数据)
|
||||
|
||||
现规定,添加品牌数据的 api 接口说明如下:
|
||||
|
||||

|
||||
|
||||
|
||||
代码如下:(在上一段代码的基础之上,添加代码)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
#app {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#tb {
|
||||
width: 800px;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#tb th {
|
||||
background-color: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#tb td {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
<script src="vue.js"></script>
|
||||
<script src="vue-resource121.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<input type="text" v-model="pname">
|
||||
<button @click="adddata">添加数据</button>
|
||||
|
||||
<table id="tb">
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
pname: '', //这个 pname 是我在输入框里添加的数据。我们要把这个传给服务器
|
||||
list: []
|
||||
},
|
||||
// Vue对象实例创建成功以后就会自动调用这个方法
|
||||
created: function () {
|
||||
this.getlist();
|
||||
},
|
||||
methods: {
|
||||
//ajax请求:添加数据
|
||||
adddata: function () {
|
||||
// 1、获取用户填写的文本框的值只需要通过this.pname即可
|
||||
// 2、调用ajax的post方法将数据上传到服务器
|
||||
var url = 'http://vueapi.ittun.com/api/addproduct';
|
||||
var postData = { name: this.pname }; //【重要】键`name`是json中约定好的字段。我们把这个字段传给服务器
|
||||
var options = { emulateJSON: true };
|
||||
this.$http.post(url, postData, options).then(function (response) {
|
||||
if (response.body.status != 0) {
|
||||
alert(response.body.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.pname = '';
|
||||
|
||||
// 3、添加完成后,只需要手动再调用一次getlist(将列表数据重新加载一次),即可刷新页面上的数据
|
||||
this.getlist();
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
//ajax请求:获取数据
|
||||
getlist: function () {
|
||||
this.$http.get('http://vueapi.ittun.com/api/getprodlist')
|
||||
.then(function (response) {
|
||||
// 1、处理服务器异常信息提示
|
||||
if (response.body.status != 0) {
|
||||
alert(response.body.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2、处理正常的数据逻辑
|
||||
this.list = response.body.message;
|
||||
console.log(this.list);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
**代码举例**:(删除数据)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<style>
|
||||
#app {
|
||||
width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#tb {
|
||||
width: 800px;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#tb th {
|
||||
background-color: #0094ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#tb td {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
<script src="vue.js"></script>
|
||||
<script src="vue-resource121.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<input type="text" v-model="pname">
|
||||
<button @click="adddata">添加数据</button>
|
||||
|
||||
<table id="tb">
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>名称</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
<tr v-for="item in list">
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.ctime}}</td>
|
||||
<td>
|
||||
<!-- 具体要删除哪个item,不能写死。所以要根据id来删 -->
|
||||
<a href="javascript:void(0)" @click="deldata(item.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
pname: '', //这个 pname 是我在输入框里添加的数据。我们要把这个传给服务器
|
||||
list: []
|
||||
},
|
||||
// Vue对象实例创建成功以后就会自动调用这个方法
|
||||
created: function () {
|
||||
this.getlist();
|
||||
},
|
||||
methods: {
|
||||
//ajax请求:添加数据
|
||||
adddata: function () {
|
||||
// 1、获取用户填写的文本框的值只需要通过this.pname即可
|
||||
// 2、调用ajax的post方法将数据上传到服务器
|
||||
var url = 'http://vueapi.ittun.com/api/addproduct';
|
||||
var postData = { name: this.pname }; //【重要】键`name`是json中约定好的字段。我们把这个字段传给服务器
|
||||
var options = { emulateJSON: true };
|
||||
this.$http.post(url, postData, options).then(function (response) {
|
||||
if (response.body.status != 0) {
|
||||
alert(response.body.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.pname = '';
|
||||
|
||||
// 3、直接将列表数据重新加载一次,即可刷新页面上的数据
|
||||
this.getlist();
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
//ajax请求:获取数据
|
||||
getlist: function () {
|
||||
this.$http.get('http://vueapi.ittun.com/api/getprodlist')
|
||||
.then(function (response) {
|
||||
// 1、处理服务器异常信息提示
|
||||
if (response.body.status != 0) {
|
||||
alert(response.body.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2、处理正常的数据逻辑
|
||||
this.list = response.body.message;
|
||||
console.log(this.list);
|
||||
});
|
||||
},
|
||||
|
||||
// ajax请求:删除数据
|
||||
deldata: function (id) {
|
||||
this.$http.get('http://vueapi.ittun.com/api/delproduct/' + id)
|
||||
.then(function (response) {
|
||||
if (response.body.status != 0) {
|
||||
alert(response.body.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新列表
|
||||
this.getlist();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||
### jsonp
|
||||
|
||||

|
||||
|
||||
|
||||
**格式举例**:
|
||||
|
||||
```javascript
|
||||
// 利用vue-resource中的jsonp方法实现跨域请求数据,这里要注意的是:
|
||||
// url后面不需要跟callback=fn这个参数了,jsonp方法会自动加上
|
||||
this.$http.jsonp('http://vuecms.ittun.com/api/getlunbo?id=1')
|
||||
.then(function (response) {
|
||||
console.log(JSON.stringify(response.body));
|
||||
}, function (err) {
|
||||
//err是异常数据
|
||||
});
|
||||
```
|
||||
|
||||
请求结果:
|
||||
|
||||

|
||||
|
||||
|
||||
## JSONP的实现原理
|
||||
|
||||
由于浏览器的安全性限制,默认不允许Ajax发起跨域(协议不同、域名不同、端口号不同)的请求。浏览器认为这种访问不安全。
|
||||
|
||||
**JSONP的实现原理**:通过动态创建script标签的形式,用script标签的src属性,代表api接口的url,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓,JSONP只支持Get请求)。
|
||||
|
||||
|
||||
具体实现过程:
|
||||
|
||||
|
||||
- 先在客户端定义一个回调方法,预定义对数据的操作
|
||||
|
||||
- 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的api接口;
|
||||
|
||||
- 服务器api接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行;
|
||||
|
||||
- 客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了
|
||||
|
||||
|
||||
## axios
|
||||
|
||||
除了 vue-resource 之外,还可以使用 `axios` 的第三方包实现实现数据的请求。
|
||||
|
||||
|
||||
## 通过Vue全局配置api接口的url地址
|
||||
|
||||
api接口的url地址包括:绝对路径+相对路径。
|
||||
|
||||
我们在做Ajax请求的时候,所填写的url建议填**相对路径**,然后把**绝对路径**放在全局的位置。
|
||||
|
||||
Vue就提供了这个功能。举例如下:
|
||||
|
||||
```html
|
||||
|
||||
<script>
|
||||
// 如果我们通过全局配置了,请求的数据接口 根域名,则 ,在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 / ,否则 不会启用根路径做拼接;
|
||||
Vue.http.options.root = 'http://smyhvae/';
|
||||
// 全局启用 emulateJSON 选项
|
||||
Vue.http.options.emulateJSON = true;
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
name: '',
|
||||
list: [ // 存放所有品牌列表的数组
|
||||
]
|
||||
},
|
||||
created() { // 当 vm 实例 的 data 和 methods 初始化完毕后,vm实例会自动执行created 这个生命周期函数
|
||||
this.getAllList()
|
||||
},
|
||||
methods: {
|
||||
getAllList() { // 获取所有的品牌列表
|
||||
// 分析:
|
||||
// 1. 由于已经导入了 Vue-resource这个包,所以 ,可以直接通过 this.$http 来发起数据请求
|
||||
// 2. 根据接口API文档,知道,获取列表的时候,应该发起一个 get 请求
|
||||
// 3. this.$http.get('url').then(function(result){})
|
||||
// 4. 当通过 then 指定回调函数之后,在回调函数中,可以拿到数据服务器返回的 result
|
||||
// 5. 先判断 result.status 是否等于0,如果等于0,就成功了,可以 把 result.message 赋值给 this.list ; 如果不等于0,可以弹框提醒,获取数据失败!
|
||||
|
||||
this.$http.get('api/getprodlist').then(result => {
|
||||
// 注意: 通过 $http 获取到的数据,都在 result.body 中放着
|
||||
var result = result.body
|
||||
if (result.status === 0) {
|
||||
// 成功了
|
||||
this.list = result.message
|
||||
} else {
|
||||
// 失败了
|
||||
alert('获取数据失败!')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
如上方代码所示,第一步是在全局的位置写**绝对路径**:
|
||||
|
||||
```javascript
|
||||
Vue.http.options.root = 'http://smyhvae/';
|
||||
```
|
||||
|
||||
第二步是在Ajax请求的url中写**相对路径**:(注意,前面不要带`/`)
|
||||
|
||||
```javascript
|
||||
this.$http.get('api/getprodlist')
|
||||
```
|
||||
|
||||
|
||||
|
||||
1151
12-Vue基础/04-Vue动画.md
Normal file
1151
12-Vue基础/04-Vue动画.md
Normal file
File diff suppressed because it is too large
Load Diff
881
12-Vue基础/05-Vue组件的定义和注册.md
Normal file
881
12-Vue基础/05-Vue组件的定义和注册.md
Normal file
@@ -0,0 +1,881 @@
|
||||
|
||||
> 本文最初发表于[博客园](https://www.cnblogs.com/smyhvae/p/9195261.html),并在[GitHub](https://github.com/smyhvae/Web)上持续更新**前端的系列文章**。欢迎在GitHub上关注我,一起入门和进阶前端。
|
||||
|
||||
> 以下是正文。
|
||||
|
||||
## 前言
|
||||
|
||||
### 什么是组件
|
||||
|
||||
**组件**: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。
|
||||
|
||||
|
||||
### 模块化和组件化的区别
|
||||
|
||||
- 模块化:是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一
|
||||
|
||||
- 组件化:是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用
|
||||
|
||||
|
||||
## 全局组件的定义和注册
|
||||
|
||||
组件`Component`是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。
|
||||
|
||||
全局组件的定义和注册有三种方式,我们接下来讲一讲。
|
||||
|
||||
### 写法一
|
||||
|
||||
写法一:使用Vue.extend方法定义组件,使用 Vue.component方法注册组件。
|
||||
|
||||
代码举例:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 如果要使用组件,直接把组件的名称,以 HTML 标签的形式,引入到页面中,即可 -->
|
||||
<account> </account>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
//第一步:使用 Vue.extend 定义组件
|
||||
var myAccount = Vue.extend({
|
||||
template: '<div><h2>登录页面</h2> <h3>注册页面</h3></div>' // 通过 template 属性,指定了组件要展示的HTML结构。template 是 Vue 中的关键字,不能改。
|
||||
});
|
||||
//第二步:使用 Vue.component 注册组件
|
||||
// Vue.component('组件的名称', 创建出来的组件模板对象)
|
||||
Vue.component('account', myAccount); //第一个参数是组件的名称(标签名),第二个参数是模板对象
|
||||
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上方代码中,在注册组件时,第一个参数是标签名,第二个参数是组件的定义。
|
||||
|
||||
运行结果如下:
|
||||
|
||||

|
||||
|
||||
代码截图如下:
|
||||
|
||||

|
||||
|
||||
上图中,注意两点:
|
||||
|
||||
**注意1**、红框部分,要保证二者的名字是一致的。如果在注册时,组件的名称是**驼峰命名**,比如:
|
||||
|
||||
```javascript
|
||||
Vue.component('myComponent', myAccount); //第一个参数是组件的名称(标签名),第二个参数是模板对象
|
||||
```
|
||||
|
||||
那么,在标签中使用组件时,需要把大写的驼峰改为小写的字母,同时两个单词之间使用`-`进行连接:
|
||||
|
||||
```html
|
||||
<my-component> </my-component>
|
||||
|
||||
```
|
||||
|
||||
所以,为了避免名字不一致的问题,我们注册组件时,组件的名称可以直接写成`my-component`。比如:(避免驼峰不一致的建议写法)
|
||||
|
||||
```javascript
|
||||
Vue.component('my-component', myAccount);
|
||||
```
|
||||
|
||||
|
||||
|
||||
**注意2**、绿框部分,一定要用一个大的根元素(例如`<div>`)包裹起来。如果我写成下面这样,就没有预期的效果:
|
||||
|
||||
```
|
||||
template: '<h2>登录页面</h2> <h3>注册页面</h3>'
|
||||
```
|
||||
|
||||
结果如下:(并非预期的效果)
|
||||
|
||||

|
||||
|
||||
### 写法二
|
||||
|
||||
写法二:Vue.component方法定义、注册组件(一步到位)。
|
||||
|
||||
代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<account> </account>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//定义、注册组件:第一个参数是组件的名称(标签名),第二个参数是组件的定义
|
||||
Vue.component('account', {
|
||||
template: '<div><h2>登录页面</h2> <h3>注册页面</h3></div>' // template 是 Vue 中的关键字,不能改。
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
代码截图如下:
|
||||
|
||||

|
||||
|
||||
上图中,同样注意两点:
|
||||
|
||||
1、红框部分,要保证二者的名字是一致的。
|
||||
|
||||
2、绿框部分,一定要用一个大的根元素(例如`<div>`)包裹起来。如果我写成下面这样,就没有预期的效果:
|
||||
|
||||
```
|
||||
template: '<h2>登录页面</h2> <h3>注册页面</h3>'
|
||||
```
|
||||
|
||||
结果如下:(并非预期的效果)
|
||||
|
||||

|
||||
|
||||
|
||||
### 写法三【荐】
|
||||
|
||||
> 上面的写法一、写法二并不是很智能,因为在定义模板的时候,没有智能提示和高亮,容易出错。我们不妨来看看写法三。
|
||||
|
||||
写法三:将组件内容定义到template标签中去。
|
||||
|
||||
代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 定义模板 -->
|
||||
<template id="myAccount">
|
||||
<div>
|
||||
<h2>登录页面</h2>
|
||||
<h3>注册页面</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div id="app">
|
||||
<!-- 使用组件 -->
|
||||
<account> </account>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//定义、注册组件
|
||||
Vue.component('account', {
|
||||
template: '#myAccount' // template 是 Vue 中的关键字,不能改。
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
代码截图如下:
|
||||
|
||||

|
||||
|
||||
写法三其实和方法二差不多,无非是把绿框部分的内容,单独放在了`<template>`标签中而已,这样有利于 html 标签的书写。
|
||||
|
||||
|
||||
## 使用components定义私有组件
|
||||
|
||||
我们在上一段中定义的是**全局组件**,这样做的时候,多个Vue实例都可以使用这个组件。
|
||||
|
||||
我们还可以在一个Vue实例的内部定义**私有组件**,这样做的时候,只有当前这个Vue实例才可以使用这个组件。
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 使用Vue实例内部的私有组件 -->
|
||||
<my-login></my-login>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
components: { // 定义、注册Vue实例内部的私有组件
|
||||
myLogin: {
|
||||
template: '<h3>这是私有的login组件</h3>'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行效果:
|
||||
|
||||

|
||||
|
||||
【荐】当然,我们还可以把**模板的定义**存放在`<template>`标签中,这样的话,模板里的html标签就可以出现智能提示和高亮,避免出错。如下:
|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- 定义模板 -->
|
||||
<template id="loginTmp">
|
||||
<h3>这是私有的login组件</h3>
|
||||
</template>
|
||||
|
||||
<div id="app">
|
||||
<!-- 调用Vue实例内部的私有组件 -->
|
||||
<my-login></my-login>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
components: { // 定义、注册Vue实例内部的私有组件
|
||||
myLogin: {
|
||||
template: '#loginTmp'
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行效果不变。
|
||||
|
||||
上方代码中,如果在注册私有组件时,组件的名称是**驼峰命名**,比如:
|
||||
|
||||
```javascript
|
||||
components: { // 定义、注册Vue实例内部的私有组件
|
||||
myLogin: {
|
||||
template: '#loginTmp'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
那么,在标签中使用组件时,需要把大写的驼峰改为小写的字母,同时两个单词之间使用`-`进行连接:
|
||||
|
||||
```html
|
||||
<my-login></my-login>
|
||||
```
|
||||
|
||||
所以,为了避免名字不一致的问题,我们注册组件时,组件的名称可以直接写成`my-login`。比如:(避免驼峰不一致的建议写法)
|
||||
|
||||
|
||||
```javascript
|
||||
components: { // 定义、注册Vue实例内部的私有组件
|
||||
`my-login`: {
|
||||
template: '#loginTmp'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 为组件添加 data 和 methods
|
||||
|
||||
既然组件是一个页面,那么,页面中可能会有一些功能要**动态展示**。因此,我们有必要为组件添加 data 和 methods。
|
||||
|
||||
代码举例如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 定义组件的模板 -->
|
||||
<template id="myAccount">
|
||||
<div>
|
||||
<!-- 在组件的模板中,调用本组件中的data -->
|
||||
{{myData}}
|
||||
<a href="#" v-on:click="login">登录1</a>
|
||||
<h2>登录页面</h2>
|
||||
<h3>注册页面</h3>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div id="app">
|
||||
<!-- 第一次调用组件 -->
|
||||
<account> </account>
|
||||
<!-- 第二次调用组件 -->
|
||||
<account> </account>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//定义、注册组件
|
||||
Vue.component('account', {
|
||||
template: '#myAccount',
|
||||
//组件中的 data
|
||||
//【注意】组件中的data,不再是对象,而是一个方法(否则报错);而且这个方法内部,还必须返回一个对象才行
|
||||
// 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!
|
||||
data: function () {
|
||||
return {
|
||||
myData: 'smyhvae'
|
||||
}
|
||||
},
|
||||
//组件中的 method
|
||||
methods: {
|
||||
login: function () {
|
||||
alert('login操作');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
上方代码所示,我们在`account`组件中添加的data 和 methods,其**作用域**只限于`account`组件里,保证独立性。
|
||||
|
||||
注意,在为组件添加数据时,data不再是对象了,而是function,而且要通过 return的形式进行返回;否则,页面上是无法看到效果的。通过 function返回对象的形式来定义data,作用是:
|
||||
|
||||
- 上方代码中,组件`<account>`被调用了两次(不像根组件那样只能调用一次),但是每个组件里的数据 myData是**各自独立**的,不产生冲突。
|
||||
|
||||
- 换而言之,通过函数返回对象的目的,是为了让每个组件都有自己**独立的数据存储**,而不应该共享一套数据。
|
||||
|
||||
|
||||
### 为什么组件的data必须是一个function
|
||||
|
||||
|
||||
我们先来看下面这样的例子:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 第一次调用组件 -->
|
||||
<counter></counter>
|
||||
<hr>
|
||||
|
||||
<!-- 第二次调用组件 -->
|
||||
<counter></counter>
|
||||
</div>
|
||||
|
||||
<!-- 定义模板 -->
|
||||
<template id="tmpl">
|
||||
<div>
|
||||
<input type="button" value="让count加1" @click="increment">
|
||||
<h3>{{count}}</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var dataObj = { count: 0 }
|
||||
|
||||
// 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
|
||||
Vue.component('counter', {
|
||||
template: '#tmpl',
|
||||
data: function () {
|
||||
return dataObj //当我们return全局的dataObj的时候,子组件们会共享这个dataObj
|
||||
},
|
||||
methods: {
|
||||
increment() {
|
||||
this.count++
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行效果如下:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
上面的例子中,将组件`<counter>`调用了两次,由于`dataObj`是**全局对象**,导致两个组件实例都可以**共享**这个`dataObj`数据。于是,我们点击任何一个组件实例的按钮,都可以让`count`数据加1。
|
||||
|
||||
|
||||
现在问题来了,如果我们想让组件`<counter>`的两个实例去单独操作`count`数据,应该怎么做呢?我们应该修改 data中 return出去的内容:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<counter></counter>
|
||||
<hr>
|
||||
<counter></counter>
|
||||
<hr>
|
||||
<counter></counter>
|
||||
</div>
|
||||
|
||||
<template id="tmpl">
|
||||
<div>
|
||||
<input type="button" value="让count加1" @click="increment">
|
||||
<h3>{{count}}</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var dataObj = { count: 0 }
|
||||
|
||||
// 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
|
||||
Vue.component('counter', {
|
||||
template: '#tmpl',
|
||||
data: function () {
|
||||
// return dataObj //当我们return全局的dataObj的时候,这个dataObj是共享的
|
||||
return { count: 0 } // 【重要】return一个**新开辟**的对象数据
|
||||
},
|
||||
methods: {
|
||||
increment() {
|
||||
this.count++
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
运行效果:
|
||||
|
||||

|
||||
|
||||
如上图所示,每当我们创建一个新的组件实例时,就会调用data函数,data函数里会return一个**新开辟**的对象数据。这样做,就可以保证每个组件实例有**独立的数据存储**。
|
||||
|
||||
## 组件的切换
|
||||
|
||||
|
||||
### 使用v-if和v-else结合flag进行切换
|
||||
|
||||
|
||||
代码举例:(登录组件/注册组件,二选一)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 温馨提示:`.prevent`可以阻止超链接的默认事件 -->
|
||||
<a href="" @click.prevent="flag=true">登录</a>
|
||||
<a href="" @click.prevent="flag=false">注册</a>
|
||||
|
||||
<!-- 登录组件/注册组件,同时只显示一个 -->
|
||||
<login v-if="flag"></login>
|
||||
<register v-else="flag"></register>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
Vue.component('login', {
|
||||
template: '<h3>登录组件</h3>'
|
||||
})
|
||||
|
||||
Vue.component('register', {
|
||||
template: '<h3>注册组件</h3>'
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
flag: false
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
运行效果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
### 使用Vue提供的`<component>`标签实现组件切换
|
||||
|
||||
上面的例子中,我们是通过flag的值来进行组件的切换。但是,flag的值只可能有两种情况,也就是说,v-if和v-else只能进行两个组件之间的切换。
|
||||
|
||||
那如何实现三个甚至三个以上的组件切换呢?这里,我们可以用到Vue提供的`<component>`标签。
|
||||
|
||||
|
||||
我们先来看一下`<component>`标签的用法。
|
||||
|
||||
基于上面的代码,如果我想让login组件显示出来,借助`<component>`标签可以这样做:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="Vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- Vue提供了 component ,来展示对应名称的组件 -->
|
||||
<!-- 【重要】component 是一个占位符, `:is` 属性,可以用来指定要展示的组件名称。这里,我们让 login 组件显示出来 -->
|
||||
<component :is="'login'"></component>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 组件名称是 字符串
|
||||
Vue.component('login', {
|
||||
template: '<h3>登录组件</h3>'
|
||||
})
|
||||
|
||||
Vue.component('register', {
|
||||
template: '<h3>注册组件</h3>'
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上方代码中,提取关键代码如下:
|
||||
|
||||
```html
|
||||
<component :is="'login'"></component>
|
||||
```
|
||||
|
||||
|
||||
如果我想让register组件显示出来,借助`<component>`标签可以这样做:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="Vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- Vue提供了 component ,来展示对应名称的组件 -->
|
||||
<!-- 【重要】component 是一个占位符, `:is` 属性,可以用来指定要展示的组件名称 -->
|
||||
<component :is="'register'"></component>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 组件名称是 字符串
|
||||
Vue.component('login', {
|
||||
template: '<h3>登录组件</h3>'
|
||||
})
|
||||
|
||||
Vue.component('register', {
|
||||
template: '<h3>注册组件</h3>'
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
上方代码中,提取关键代码如下:
|
||||
|
||||
```html
|
||||
<component :is="'register'"></component>
|
||||
```
|
||||
|
||||
因此,如果要实现组件之间的切换,我们可以给`<component>`标签里的is属性值设置为变量即可,来看看代码实现。
|
||||
|
||||
**实现组件切换**的完整代码:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 点击按钮后,设置变量`comName`为不同的值,代表着后面的component里显示不同的组件 -->
|
||||
<a href="" @click.prevent="comName='login'">登录</a>
|
||||
<a href="" @click.prevent="comName='register'">注册</a>
|
||||
|
||||
<!-- Vue提供了 component ,来展示对应名称的组件 -->
|
||||
<!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 -->
|
||||
<!-- 此处的`comName`是变量,变量值为组件名称 -->
|
||||
<component :is="comName"></component>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 组件名称是 字符串
|
||||
Vue.component('login', {
|
||||
template: '<h3>登录组件</h3>'
|
||||
})
|
||||
|
||||
Vue.component('register', {
|
||||
template: '<h3>注册组件</h3>'
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
效果:
|
||||
|
||||

|
||||
|
||||
|
||||
## 多个组件切换时,通过mode属性添加过渡的动画
|
||||
|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<style>
|
||||
.v-enter,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(150px);
|
||||
}
|
||||
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<a href="" @click.prevent="comName='login'">登录</a>
|
||||
<a href="" @click.prevent="comName='register'">注册</a>
|
||||
|
||||
<!-- 通过 mode 属性,设置组件切换时候的 过渡动画 -->
|
||||
<!-- 【重点】亮点是 mode="out-in" 这句话 -->
|
||||
<transition mode="out-in">
|
||||
<component :is="comName"></component>
|
||||
</transition>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 组件名称是 字符串
|
||||
Vue.component('login', {
|
||||
template: '<h3>登录组件</h3>'
|
||||
})
|
||||
|
||||
Vue.component('register', {
|
||||
template: '<h3>注册组件</h3>'
|
||||
})
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
|
||||
},
|
||||
methods: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行效果:
|
||||
|
||||
20180618_2240.gif
|
||||
|
||||
如上方代码所示,多个组件切换时,如果要设置动画,可以用`<transition>`把组件包裹起来。需要注意的是,我给`<transition>`标签里添加了`mode="out-in"`这种模式,它表示第一个组件消失之后,第二个组件才会出现。如果没有这个mode属性,效果如下:(第一个组件准备消失的时候,第二个组件马上就准备出现,这不是我们想要的效果)
|
||||
|
||||
20180618_2245.gif
|
||||
|
||||
|
||||
## 我的公众号
|
||||
|
||||
想学习<font color=#0000ff>**代码之外的技能**</font>?不妨关注我的微信公众号:**千古壹号**(id:`qianguyihao`)。
|
||||
|
||||
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
|
||||
|
||||

|
||||
|
||||
|
||||
650
12-Vue基础/06-Vue组件之间的传值.md
Normal file
650
12-Vue基础/06-Vue组件之间的传值.md
Normal file
@@ -0,0 +1,650 @@
|
||||
|
||||
|
||||
## 父组件向子组件传值
|
||||
|
||||
|
||||
我们可以这样理解:Vue实例就是一个**父组件**,而我们自定义的组件(包括全局组件、私有组件)就是**子组件**。
|
||||
|
||||
【重点】需要注意的是,子组件不能直接使用父组件中的数据。**父组件可以通过`props`属性向子组件传值**。
|
||||
|
||||
### 父组件向子组件传值的代码举例
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 第三步:父组件在引用子组件的时候, 通过 属性绑定(v-bind:)的形式, -->
|
||||
<!-- 把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
|
||||
<component1 v-bind:parent-msg="msg"></component1>
|
||||
</div>
|
||||
|
||||
<!-- 定义子组件的模板 -->
|
||||
<template id="myTemplate">
|
||||
<!-- 第二步:在子组件的模板中,使用props中的属性 -->
|
||||
<h2 @click="change">我是子组件。我想使用父组件中的数据parentMsg: {{ parentMsg }}</h2>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '父组件中的数据123'
|
||||
},
|
||||
methods: {},
|
||||
components: {
|
||||
// 子组件默认无法访问到 父组件中的 data 中的数据 和 methods 中的方法
|
||||
component1: { //将子组件的名称定义为 component1
|
||||
template: '#myTemplate',
|
||||
data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
|
||||
// data 上的数据,都是可读可写的
|
||||
return {
|
||||
title: '子组件私有的数据 title',
|
||||
content: '子组件私有的数据 content'
|
||||
}
|
||||
},
|
||||
// 注意: 组件中的 所有 props 中的数据,都是通过 父组件 传递给子组件的
|
||||
// props 中的数据,都是只读的,无法重新赋值
|
||||
props: ['parentMsg'], // 第一步:把父组件传递过来的 parentMsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
|
||||
directives: {},
|
||||
filters: {},
|
||||
components: {},
|
||||
methods: {
|
||||
change() {
|
||||
// 下面这行会报错,因为子组件不要直接修改父组件中的data数据
|
||||
// this.parentMsg = '被修改了'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
效果如下:
|
||||
|
||||

|
||||
|
||||
代码截图如下:
|
||||
|
||||

|
||||
|
||||
|
||||
**父组件给子组件传值的步骤**:
|
||||
|
||||
> 根据上方截图,我们可以总结出父组件给子组件传值的步骤如下。
|
||||
|
||||
(1)在子组件的`props`属性中声明父亲传递过来的数据
|
||||
|
||||
(2)定义子组件的模板时,使用props中的属性
|
||||
|
||||
(3)父组件在引用子组件时,进行属性绑定。
|
||||
|
||||
|
||||
|
||||
|
||||
**子组件中,data中的数据和props中的数据的区别**:
|
||||
|
||||
- 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上。props 中的数据,都是通过 父组件 传递给子组件的。
|
||||
|
||||
|
||||
- data中的数据是可读可写的;props中的属性只是可读的,无法重新赋值,重新赋值会报错(也就是说,子组件不要直接去修改父组件中的数据)。
|
||||
|
||||
### 父组件将方法传递给子组件
|
||||
|
||||
> 父组件通过事件绑定机制,将父组件的方法传递给子组件
|
||||
|
||||
代码举例:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 父组件向子组件 传递 方法,是通过 事件绑定机制; v-on。当我们自定义了 一个 事件属性 parent-show(这个地方不能用驼峰命名)之后,-->
|
||||
<!-- 那么,子组件就能够,通过 emit 来调用 传递进去的 这个 方法了 -->
|
||||
<!-- 【第一步】。意思是说,`show`是父组件的方法名,`parent-show`是自定义的时间属性,稍后要在子组件中用到 -->
|
||||
<component1 @parent-show='show'></component1>
|
||||
</div>
|
||||
|
||||
<!-- 定义子组件的模板 -->
|
||||
<template id="myTemplate">
|
||||
<!-- 【第二步】按照正常的写法来:点击按钮,调用子组件的方法 -->
|
||||
<div @click="childClick">我是子组件,点击调用父组件的方法</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: { //父组件的data
|
||||
// msg: '父组件中的数据'
|
||||
},
|
||||
methods: {
|
||||
show: function () { // 定义父组件的show方法
|
||||
console.log('父组件提供的方法');
|
||||
}
|
||||
},
|
||||
components: {
|
||||
component1: { //将子组件的名称定义为 component1
|
||||
template: '#myTemplate',
|
||||
data() { // 子组件的data
|
||||
return {
|
||||
// content: '子组件私有的数据 content'
|
||||
}
|
||||
},
|
||||
props: [''],
|
||||
directives: {},
|
||||
filters: {},
|
||||
components: {},
|
||||
methods: {
|
||||
childClick() {
|
||||
// 当点击子组件的按钮时,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
|
||||
// emit 英文原意: 是触发,调用、发射。意思是,触发父组件的方法
|
||||
// 【第三步】 在子组件的方法中,通过 emit 触发父组件的方法
|
||||
this.$emit('parent-show');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
效果如下:(点击子组件,触发了父组件的方法)
|
||||
|
||||

|
||||
|
||||
根据上面的代码,我们可以总结出,父组件将方法传递给子组件,分为三步,具体可以看上方代码的注释。
|
||||
|
||||
|
||||
## 子组件向父组件传值
|
||||
|
||||
上面的一段中,我们再看一遍**父组件将方法传递给子组件**的这段代码(一定要再看一遍,因为我们是要在此基础之上做修改)。
|
||||
|
||||
如果要实现**子组件向父组件传值**,代码是类似的,我们只需要在子组件通过`emit`触发父组件的方法时,把子组件的参数带出去就可以了。代码如下。
|
||||
|
||||
**代码举例1**:(将子组件中的常量传递给父组件)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<component1 @parent-show='show'></component1>
|
||||
</div>
|
||||
|
||||
<!-- 定义子组件的模板 -->
|
||||
<template id="myTemplate">
|
||||
<h2 @click="childClick">我是子组件,点击调用父组件的方法</h2>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: { //父组件的data
|
||||
// msg: '父组件中的数据'
|
||||
},
|
||||
methods: { // 定义父组件的方法
|
||||
show: function (arg1, arg2) { //【第二步】父组件里放两个参数,这个两个参数就代表着子组件中的`child 123`、`child 789`
|
||||
console.log('父组件提供的方法');
|
||||
console.log('打印子组件传递过来的参数。参数一:' + arg1 + ',参数二:'+ arg2);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
component1: { //将子组件的名称定义为 component1
|
||||
template: '#myTemplate',
|
||||
data() { // 子组件的data
|
||||
return {
|
||||
// content: '子组件私有的数据 content'
|
||||
}
|
||||
},
|
||||
props: [''],
|
||||
directives: {},
|
||||
filters: {},
|
||||
components: {},
|
||||
methods: {
|
||||
childClick() {
|
||||
// 子组件如果要给父组件传递参数,在触发 emit 的时候,通过参数的形式带出去就可以了
|
||||
// 【第一步】在子组件里,我们带两个参数出去,传给父组件
|
||||
this.$emit('parent-show', 'child 123', 'child 789');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
运行结果:(点击`<h2>`之后)
|
||||
|
||||

|
||||
|
||||
**代码举例2**:(将子组件中的data数据传递给父组件,存放到父组件的data中)
|
||||
|
||||
> 在上方代码的基础之上,做改进。
|
||||
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<component1 @parent-show='show'></component1>
|
||||
</div>
|
||||
|
||||
<!-- 定义子组件的模板 -->
|
||||
<template id="myTemplate">
|
||||
<h2 @click="childClick">我是子组件,点击调用父组件的方法</h2>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: { //父组件的data
|
||||
parentData: null
|
||||
},
|
||||
methods: { // 定义父组件的方法
|
||||
show: function (arg) { //【第二步】父组件里放参数,这个参数就代表着子组件中的 child.data
|
||||
console.log('父组件提供的方法');
|
||||
this.parentData = arg; //将参数arg传递给父组件的data,也就达到了目的:子组件传递数据,赋值给父组件
|
||||
console.log('打印父组件的数据(这是子组件传过来的):'+ JSON.stringify(this.parentData));
|
||||
}
|
||||
},
|
||||
components: {
|
||||
component1: { //将子组件的名称定义为 component1
|
||||
template: '#myTemplate',
|
||||
data() { // 子组件的data
|
||||
return {
|
||||
childData: { //定义自组件的数据
|
||||
name: 'smyhvae',
|
||||
age: 26
|
||||
}
|
||||
}
|
||||
},
|
||||
props: [''],
|
||||
directives: {},
|
||||
filters: {},
|
||||
components: {},
|
||||
methods: {
|
||||
childClick() {
|
||||
// 子组件如果要给父组件传递参数,在触发 emit 的时候,通过参数的形式带出去就可以了
|
||||
// 【第一步】在子组件里,通过传参的形式,把子组件的data,传给父组件
|
||||
this.$emit('parent-show', this.childData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||
运行结果:(点击`<h2>`之后)
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## 案例:发表评论功能的实现
|
||||
|
||||
> 该案例需要完善,目前只是为了演示 localStorage
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<link rel="stylesheet" href="bootstrap3.3.7.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
|
||||
<cmt-box @func="loadComments"></cmt-box>
|
||||
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" v-for="item in list" :key="item.id">
|
||||
<span class="badge">评论人: {{ item.user }}</span>
|
||||
{{ item.content }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<template id="tmpl">
|
||||
<div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>评论人:</label>
|
||||
<input type="text" class="form-control" v-model="user">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>评论内容:</label>
|
||||
<textarea class="form-control" v-model="content"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
var commentBox = {
|
||||
data() {
|
||||
return {
|
||||
user: '',
|
||||
content: ''
|
||||
}
|
||||
},
|
||||
template: '#tmpl',
|
||||
methods: {
|
||||
postComment() { // 发表评论的方法
|
||||
// 分析:发表评论的业务逻辑
|
||||
// 提示:评论数据存到哪里去??? 存放到了 localStorage 中 localStorage.setItem('cmts', '')
|
||||
// 1. 先组织出一个最新的评论数据对象
|
||||
// 2. 想办法,把 第一步中,得到的评论对象,保存到 localStorage 中(注意:localStorage 只支持存放字符串数据, 因此要先调用 JSON.stringify)
|
||||
// 2.1 在保存 最新的 评论数据之前,要先从 localStorage 获取到之前的评论数据(string), 转换为 一个 数组对象, 然后,把最新的评论, push 到这个数组
|
||||
// 注意:如果获取到的 localStorage 中的 评论字符串,为空不存在, 则 可以 返回一个 '[]'
|
||||
// 2.2 把 最新的 评论列表数组,再次调用 JSON.stringify 转为 数组字符串,然后调用 localStorage.setItem()
|
||||
|
||||
var comment = { id: Date.now(), user: this.user, content: this.content }
|
||||
|
||||
// 第一步:一开始,从 localStorage 中获取已存在的评论
|
||||
var list = JSON.parse(localStorage.getItem('cmts') || '[]') //获取已存在的评论数据。【重要】需要考虑空字符串的可能性,否则返回的是undefined
|
||||
// 第二步:添加新的评论item
|
||||
list.unshift(comment)
|
||||
// 第三步:重新保存最新的 评论数据 到 localStorage 中
|
||||
localStorage.setItem('cmts', JSON.stringify(list))
|
||||
|
||||
this.user = this.content = ''
|
||||
|
||||
// this.loadComments() // ?????
|
||||
this.$emit('func')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: [
|
||||
{ id: Date.now(), user: '李白', content: '天生我材必有用' },
|
||||
{ id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' },
|
||||
{ id: Date.now(), user: '小马', content: '我姓马, 风吹草低见牛羊的马' }
|
||||
]
|
||||
},
|
||||
beforeCreate() { // 注意:这里不能调用 loadComments 方法,因为在执行这个钩子函数的时候,data 和 methods 都还没有被初始化好
|
||||
|
||||
},
|
||||
created() {
|
||||
//页面一开始加载的时候,就去读取 localStorage 中已存在的评论list
|
||||
this.loadComments()
|
||||
},
|
||||
methods: {
|
||||
loadComments() { // 从本地的 localStorage 中,加载评论列表
|
||||
|
||||
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
|
||||
this.list = list
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'cmt-box': commentBox
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
上面的代码中,父组件定义了`loadComments()`方法,作用是**加载 localStorage 中的评论列表**。我们可以看到,页面在一开始加载的时候,就在create()生命周期中调用了`loadComments()`;当自组件中添加了评论之后,再次调用了`loadComments()`。
|
||||
|
||||
**待改进**:
|
||||
|
||||
不过,这段代码还有些问题:页面一开始加载的时候,读取的是 localStorage 中的评论列表。如果一开始的时候,从网络获取了已存在的列表,岂不是读不到了?
|
||||
|
||||
正确的做法应该是:父组件和子组件共享 list数据,每当在子组件中 添加了一条评论之后,就往 list 中添加一条 item。
|
||||
|
||||
|
||||
## 在Vue中,通过 ref 属性获取DOM元素
|
||||
|
||||
我们当然可以使用JS原生的做法(document.getElementById)或者 jQuery 来获取DOM,但是这种做法却在无形中操作了DOM,在Vue框架中并不推荐这种做法。
|
||||
|
||||
我们可以通过`ref`属性获取DOM元素。
|
||||
|
||||
`ref`的英文单词是**reference**,表示**引用**。我们平时可以经常看到控制台会报错**referenceError**的错误,就和引用类型的数据有关。
|
||||
|
||||
|
||||
**在Vue中,通过 ref 属性获取DOM元素**的步骤:
|
||||
|
||||
(1)第一步:在标签中给 DOM 元素设置 ref 属性。
|
||||
|
||||
```html
|
||||
<h3 id="myH3" ref="myTitle"> 今天天气太好了</h3>
|
||||
```
|
||||
|
||||
(2)第二步:通过 this.this.$refs.xxx 获取 DOM 元素
|
||||
|
||||
```javascript
|
||||
console.log(this.$refs.myTitle.innerText)
|
||||
```
|
||||
|
||||
**举例如下**:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- 第一步:在标签中给 DOM 元素设置 ref 属性 -->
|
||||
<h3 id="myH3" ref="myTitle"> 今天天气太好了</h3>
|
||||
|
||||
<input type="button" value="按钮元素" @click="getElement" ref="myBtn">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var login = {
|
||||
template: '<h1>登录组件</h1>',
|
||||
data() {
|
||||
return {
|
||||
msg: 'son msg'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
console.log('调用了子组件的方法')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
methods: {
|
||||
getElement() {
|
||||
// 原生js获取DOM元素
|
||||
// console.log(document.getElementById('myTitle').innerText)
|
||||
|
||||
// 第二步:通过 this.this.$refs.xxx 获取 DOM 元素
|
||||
console.log(this.$refs.myTitle.innerText)
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
components: {
|
||||
login
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行上方代码,然后我们在控制台输入`vm`,就可以看到:
|
||||
|
||||

|
||||
|
||||
|
||||
### 使用 ref 属性获取整个子组件
|
||||
|
||||
根据上面的例子,我们可以得出**规律**:只要`ref`属性加在了DOM元素身上,我们就可以获取这个DOM元素。
|
||||
|
||||
|
||||
那我们可以通过ref属性获取整个**Vue子组件**吗?当然可以。这样做的意义是:**在父组件中通过`ref`属性拿到了子组件之后,就可以进一步拿到子组件中的data和method。
|
||||
|
||||
|
||||
|
||||
|
||||
举例:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<input type="button" value="点击按钮" @click="getElement">
|
||||
|
||||
<login-component ref="loginTemplate"></login-component>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
// 创建 Vue 实例,得到 ViewModel
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {},
|
||||
methods: {
|
||||
getElement() {
|
||||
|
||||
//在父组件中,通过ref获取整个子组件,进而获取子组件的data
|
||||
console.log(this.$refs.loginTemplate.myData)
|
||||
|
||||
//在父组件中,通过ref获取整个子组件,进而获取子组件的method
|
||||
this.$refs.loginTemplate.showMethod()
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'login-component': {
|
||||
template: '<h1>登录组件</h1>',
|
||||
data() {
|
||||
return {
|
||||
myData: '子组件的data'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showMethod() {
|
||||
console.log('调用子组件的method')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
运行代码,点击按钮后,效果如下:
|
||||
|
||||

|
||||
|
||||
我们直接在控制台输入`vm`,可以看到:
|
||||
|
||||

|
||||
|
||||
53
12-Vue基础/07-Vue-router路由.md
Normal file
53
12-Vue基础/07-Vue-router路由.md
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
## 什么是路由
|
||||
|
||||
|
||||
### 后端路由
|
||||
|
||||
对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源。
|
||||
|
||||
当前端输入url请求资源时,服务器会监听到是什么url地址,那后端会返回什么样的资源呢?后端这个处理的过程就是通过**路由**来**分发**的。
|
||||
|
||||
**总结**:后端路由,就是把所有url地址都对应到服务器的资源,这个**对应关系**就是路由。
|
||||
|
||||
### 前端路由
|
||||
|
||||
对于单页面应用程序来说,主要通过URL中的`hash`(url地址中的#号)来实现不同页面之间的切换。
|
||||
|
||||
同时,hash有一个特点:HTTP请求中不会包含hash相关的内容。所以,单页面程序中的页面跳转主要用hash实现。
|
||||
|
||||
**总结**:在**单页应用**程序中,这种通过`hash`改变来**切换页面**的方式,称作前端路由(区别于后端路由)。
|
||||
|
||||
## 安装Vue-router的两种方式
|
||||
|
||||
- 官方文档:<https://router.vuejs.org/zh/>
|
||||
|
||||
|
||||
**方式一**:直接下载文件
|
||||
|
||||
下载网址:<https://unpkg.com/vue-router/dist/vue-router.js>
|
||||
|
||||
|
||||
下载之后,放进项目工程,然后我们在引入`vue.js`之后,再引入`vue-router.js`即可:
|
||||
|
||||
```html
|
||||
<script src="vue2.5.16.js"></script>
|
||||
<script src="vue-router3.0.1.js"></script>
|
||||
```
|
||||
|
||||
|
||||
然后,我们就可以在 window全局对象中使用 VueRouter这个对象。具体解释可以看接下来的代码中的注释。
|
||||
|
||||
注意,只要我们导入了`vue-router.js`这个包,在浏览器中打开网页时,url后面就会显示`#`这个符号。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
6
12-Vue基础/Vue-router路由.md
Normal file
6
12-Vue基础/Vue-router路由.md
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
## 前言
|
||||
|
||||
路由:就是SPA(单页应用)的**路径管理器**。
|
||||
|
||||
47
12-Vue基础/Vue开发积累.md
Normal file
47
12-Vue基础/Vue开发积累.md
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
|
||||
|
||||
### 动态添加对象的属性
|
||||
|
||||
- Vue中,动态新增对象的属性时,不能直接添加。正确的做法是:Vue.set(obj,key,value)。参考链接:[#](https://blog.csdn.net/tian361zyc/article/details/72909187)
|
||||
|
||||
|
||||
|
||||
|
||||
### 判断一个checkbox是否被选中
|
||||
|
||||
```html
|
||||
<!-- v-model里的内容是变量,变量里的值可能是 true 后者 false -->
|
||||
<input type="checkbox" v-model="isSelected">
|
||||
|
||||
<!-- 选中时,值为 true。未选中时,值为 false -->
|
||||
<span>{{isSelected}}</span>
|
||||
|
||||
|
||||
<!-- 选中时,显示文字。未选中时,隐藏文字 -->
|
||||
<span v-if="isSelected">haha</span>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 多个checkbox的全选和反选
|
||||
|
||||
现在有多个checkbox的item在一个数组中,另外还有一个“全选”的checkbox按钮。
|
||||
|
||||
**点击全选按钮,让子item全部选中**:
|
||||
|
||||
采用 watch 监听全选按钮,然后改变子item。
|
||||
|
||||
**当子item全部被选中时,触发全选按钮**:
|
||||
|
||||
采用 computed 计算子item 的状态,存放到变量 allChecked 中,然后用 watch 监听 allChecked 的值。
|
||||
|
||||
参考链接:
|
||||
|
||||
- [问Vue.js 如何在 data 里含数组的情况下,监听数组内指定属性的变化?](https://segmentfault.com/q/1010000014514160/a-1020000014514452)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
618
12-Vue基础/Vue组件.md
Normal file
618
12-Vue基础/Vue组件.md
Normal file
@@ -0,0 +1,618 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 子组件的定义和注册
|
||||
|
||||
我们在本文的第一段中,通过`Vue.component`形式定义的是**全局组件**。这一段中,我们来讲一下**子组件**。
|
||||
|
||||
### 在父组件中定义子组件
|
||||
|
||||
比如说,一个`账号`模块是父组件,里面分为`登陆`模块和`注册`模块,这两个晓得模块就可以定义为子组件。
|
||||
|
||||
需要注意的是作用域的问题:我们在父组件中定义的子组件,只能在当前父组件的模板中使用;在其他的组件,甚至根组件中,都无法使用。
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<account>
|
||||
|
||||
</account>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//定义、注册组件
|
||||
Vue.component('account', {
|
||||
template: '<div><h2>账号模块</h2> <login></login></div>',
|
||||
components: {
|
||||
'login': {
|
||||
template: '<h3>登录模块</h3>'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
我们发现,既然是定义父亲`<account>`的子组件,那么,子组件`<login>`的调用,只能写在父组件`<account>`的`template`模板中。
|
||||
|
||||
显示效果:
|
||||
|
||||

|
||||
|
||||
|
||||
### 在 Vue 根实例中定义子组件
|
||||
|
||||
当然,我们还可以这样做:把整个 Vue 对象当成父亲,这样的话,就可以在 Vue 示例中定义一个子组件。如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<login> </login>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
//在Vue实例中定义子组件
|
||||
components: { // components 是关键字,不能改
|
||||
'login': {
|
||||
template: '<h3>登录模块</h3>'
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
这样写的话,我们定义的子组件`<login>`在整个`#app`区域,都是可以使用的。
|
||||
|
||||
上面的代码,还有**另外一种写法**:(把子组件的模板定义,存放到变量中)【重要】
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<login> </login>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//通过变量接收定义的子组件
|
||||
var myLogin = {
|
||||
template: '<h3>登录模块</h3>' // template 是关键字,不能改
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
//在Vue实例中定义子组件
|
||||
components: { // components 是关键字,不能改
|
||||
'login': myLogin
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
注意,在定义子组件时,关键字`components`不要写错了。
|
||||
|
||||
## 组件之间的动态切换(暂略)
|
||||
|
||||
我们可以利用`<component> `标签的`:is`参数来进行组件之间的切换。
|
||||
|
||||
## 父组件向子组件传递数据
|
||||
|
||||
我们要记住:父组件通过**属性**的形式,向子组件传递数据。
|
||||
|
||||
**引入**:
|
||||
|
||||
我们先来看这样一段代码:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 这里是父组件的范围,自定义一个数值 number -->
|
||||
<counter number="1+2"> </counter>
|
||||
<counter number="10+20"> </counter>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var myCounter = {
|
||||
//【重要】这里是子组件的范围,无法直接获取父组件中的 number
|
||||
template: '<div>我是子组件。{{number}}</div>'
|
||||
}
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
components: {
|
||||
'counter': myCounter
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
上方代码中,我想把父组件里 number 的数值传递给子组件,直接这样写,是看不到效果的:
|
||||
|
||||

|
||||
|
||||
**1、父组件传值给子组件**:
|
||||
|
||||
要通过 props 属性将number进行传递给子组件才可以。代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 这里是父组件的范围,自定义一个数值 number -->
|
||||
<counter :number="1+2"> </counter>
|
||||
<counter :number="10+20"> </counter>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var myCounter = {
|
||||
//这里是子组件的范围
|
||||
props: ['number'], //通过 props 属性将父亲的 number 数据传递给子组件
|
||||
template: '<div>我是子组件。{{number}}</div>'
|
||||
}
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
components: {
|
||||
'counter': myCounter
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
在`<counter>`标签中,要注意`:number`里的冒号。加上冒号,那么引号里的内容就是表达式(期望的结果);否则,引号的内容只是字符串:
|
||||
|
||||

|
||||
|
||||
**2、子组件获取了父组件的数据后,进行求和操作**:
|
||||
|
||||
上方代码中,子组件已经获取了父组件的两个number,现在要求:每点击一次子组件,在子组件中将数据加 1。
|
||||
|
||||
一般人可能会这样写:(不推荐的写法:子组件直接修改父组件中的数据)
|
||||
|
||||
```javascript
|
||||
var myCounter = {
|
||||
//这里是子组件的范围
|
||||
props: ['number'], //通过 props 属性将父亲的数据传递给子组件
|
||||
template: '<div @click="addClick">我是子组件。{{number}}</div>',
|
||||
methods: {
|
||||
addClick: function () {
|
||||
this.number ++; //这种写法不推荐。不建议直接操作父组件中的数据
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上方代码的写法不推荐,因为不建议直接操作父组件中的数据。虽然数据操作成功,但是控制台会报错:
|
||||
|
||||
img.png
|
||||
|
||||
这样涉及到**单向数据流**的概念:
|
||||
|
||||
- 父组件可以传递参数给子组件,但是反过来,子组件不要去修改父组件传递过来的参数。因为同一个参数,可能会传递给多个子组件,避免造成修改的冲突。
|
||||
|
||||
既然如此,我可以把父组件中的数据,在子组件中创建**副本**,然后我们去修改这个副本,就不会造成影响了。最终,完整版代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 这里是父组件的范围,自定义一个数值 number -->
|
||||
<counter :number="1+2"> </counter>
|
||||
<counter :number="10+20"> </counter>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var myCounter = {
|
||||
//这里是子组件的范围
|
||||
props: ['number'], //通过 props 属性将父亲的数据传递给子组件
|
||||
data: function () {
|
||||
return {
|
||||
number2: this.number
|
||||
}
|
||||
},
|
||||
template: '<div @click="addClick">我是子组件。{{number2}}</div>',
|
||||
|
||||
methods: {
|
||||
addClick: function () {
|
||||
this.number2 ++; //操作和修改number的副本
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
components: {
|
||||
'counter': myCounter
|
||||
},
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
## 子组件向父组件传值
|
||||
|
||||
我们要记住:子组件通过**事件触发**的形式,向父组件传值。
|
||||
|
||||
|
||||
|
||||
|
||||
**案例1:**子组件给父组件传递数据
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 在外层监听`mysend`事件,进而出发外层的 getData 方法 -->
|
||||
<counter v-on:mysend="getData"> </counter>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Vue.component('counter', {
|
||||
template: '<div @click = "addClick">发送数据给父组件</div>', //当组件被点击时,触发 addClick 方法
|
||||
|
||||
methods: {
|
||||
addClick: function () {
|
||||
//第一个参数为键(注意,要小写,不能大写),第二个参数为值
|
||||
this.$emit('mysend', 'smyhvae'); //通过键`mysend`事件通知外面,将值`smyhvae`传给父组件
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
methods: {
|
||||
getData: function (input) { //通过括号里的参数,获取子组件传递过来的值
|
||||
console.log(input); //打印结果:smyhvae
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
|
||||
**案例2**:获取子组件的DOM对象
|
||||
|
||||
题目:给两个相同的子组件定义计数器,每点击一次,数值加1。然后在父组件中求和。
|
||||
|
||||
步骤(1):给两个相同的子组件定义计数器,每点击一次,数值加1。代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<counter> </counter>
|
||||
<counter> </counter>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Vue.component('counter', {
|
||||
template: '<div @click = "addClick">当前计数:{{number}}</div>', //当组件被点击时,调用 addClick 方法
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
number: 0 //给组件定义一个数据:number
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addClick: function () {
|
||||
this.number++; //定义方法:每点击一次,number 的数值加 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
步骤(2):两个子组件的数值加 1 后,通知父组件。代码如下:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 在外层监听`change`事件,进而出发外层的 myClick 方法 -->
|
||||
<counter @change="myMethod"> </counter>
|
||||
<counter @change="myMethod"> </counter>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Vue.component('counter', {
|
||||
template: '<div @click = "addClick">当前计数:{{number}}</div>', //当组件被点击时,
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
number: 0 //给组件定义一个数据:number
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addClick: function () {
|
||||
this.number++; //定义方法:每点击一次,number 的数值加 1
|
||||
this.$emit('change'); //通过这一行的`change`,通知外面,内部的 addClick 方法已经执行了
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
methods: {
|
||||
myMethod: function () {
|
||||
console.log('触发父组件');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
上方代码中,通过关键字`emit`通知父组件,子组件里的 addClick 方法被执行了。父组件得知后,执行`myMethod()`方法(这个方法是在Vue实例中定义的,很好理解)
|
||||
|
||||
步骤(3):在父组件中求和
|
||||
|
||||
既然父组件已经得知子组件的 addClick 事件,那我们直接在父组件的`myMethod()`里定义求和的方法即可。
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
<script src="vue2.5.16.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 在外层监听`change`事件,进而触发外层的 myMethod 方法 -->
|
||||
<counter ref="one" @change="myMethod"> </counter>
|
||||
<counter ref="two" @change="myMethod"> </counter>
|
||||
<div>{{totalData}}</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Vue.component('counter', {
|
||||
template: '<div @click = "addClick">当前计数:{{number}}</div>', //当组件被点击时,触发 addClick方法
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
number: 0 //给组件定义一个数据:number
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addClick: function () {
|
||||
this.number++; //定义方法:每点击一次,number 的数值加 1
|
||||
this.$emit('change'); //通过这一行自定义的`change`,通知外面,内部的 addClick 方法已经执行了
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
totalData: 0
|
||||
},
|
||||
methods: {
|
||||
myMethod: function () {
|
||||
console.log('触发父组件');
|
||||
//通过`$refs`获取子组件中各自的number数值
|
||||
var a1 = this.$refs.one.number;
|
||||
var a2 = this.$refs.two.number;
|
||||
//求和,存放在父组件的 totalData 中
|
||||
this.totalData = a1 + a2;
|
||||
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
代码的关键:
|
||||
|
||||
- 在`<counter>`标签中,通过 `ref = "xxx"`属性,给各个组件起一个别名,代表组件的引用
|
||||
|
||||
- 在父函数`myMethod()`中,通过`this.$refs.xxx`获取组件的引用。我们看一下最后两行代码在控制台的输出便知:(组件里有 number 属性)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user