目录
核心概念: 12
MVC和MVVM 17
脚手架|开发环境安装: 18
nrm工具 20
webpack 21
模块化: 26
目录结构: 26
插值表达式|绑定数据|绑定属性|绑定html|绑定class|绑定style 28
v-model 29
v-for和key 30
v-if和v-show 30
双向数据绑定 36
事件 37
watch|computed|methods区别: 42
自定义指令: 47
过滤器: 49
localStorage 51
todolist 51
组件 53
创建组件: 54
组件中的data和methods: 56
组件切换 56
父组件向子组件传值: 57
父组件向子组件传方法: 58
ref获取DOM元素: 58
ref获取组件: 59
父子组件传值-父组件给子组件传值 61
父子组件传值-父组件主动获取子组件的数据和方法 63
父子组件传值-子组件主动获取父组件的属性和方法 63
非父子组件传值 65
组件生命周期函数(钩子函数) 67
请求数据 74
vue-resource 74
axios|fetch-jsonp 78
路由 79
路由嵌套: 82
参数传递 84
配置路由: 85
动态路由 87
get传值 89
动态路由传值+请求数据,列表页|详情页 90
路由编程式导航 92
嵌套路由 93
路由模块化 95
vuex-使用state|mutations 96
vuex-使用getter 98
vuex-使用actions 98
vuex-持久化 99
UI框架-elementUI 101
vue中的动画: 103
MUI代码片断 106
ES6中Promise 110
css样式案例: 115
问题汇总: 117
vue与jquery:
jquery操作dom;
vue数据驱动,程序只与数据打交道,不直接操作dom;
注,传统方式,阻止冒泡|阻止默认行为;
核心概念:
数据驱动;
虚拟dom;
组件,组件间通信(父子间通信);
单页面框架;
基于模块化组件化的开发模式;
指令,对html元素的功能扩展,<div ng-xx></div>,
v-model,实现表单和应用状态之间的双向绑定,<input><textarea><select>元素上创建双向数据绑定,
v-if,v-else-if,v-else,条件渲染,
v-show=隐藏显示内容,只是简单地切换元素的css属性,v-show不支持<template>和v-else,
v-on,用于监听DOM事件,v-on:click=点击事件,可缩写为@click=,,v-on:mouseover鼠标移入,v-on:mouseout=鼠标移出,@dblclick=双击事件,添加一个事件监听器(绑定事件),
v-for,循环,可绑定数组的数据来渲染一个项目,支持一个可选的第2个参数如(item,index) in items,可用of替代in,可遍历对象(value,name,index) in object,
v-bind,v-bind:src=可缩写为:src,v-bind:title=,v-bind:href=,冒号后为参数,v-bind指令可用于响应式地更新HTML特性 ,绑定元素;
过滤器;
状态管理;
vue代码结构;
el指定要控制的区域,
data是个对象,指定了控制的区域内要用到的数据,
methods,虽加了s但是个对象,
在vm实例中,如果要访问data上的数据,或要用methods中的方法,要带this.
vm实例中,要获取data中数据,或调用methods中方法,必须通过this.数据属性名或this.方法名,来进行访问,这里的this就是new出来的vm实例对象;
vm实例,会监听自己身上data中所有数据的改变,只要数据一发生变化,就自动把新的数据从data上同步到页面中去,好处:程序员只用关心数据,不需考虑如何重新渲染页面;
setInterval(FUNC, 400);
clearInterval();
例:
<script>
window.notallow=function(){ #index.html页面加载完后才执行此段
new Vue({
el: "#root",
data: { #在div id为root里能使用data中定义的变量
name: "jack",
age: 20,
arr: ['a','b','c'],
obj: {name: 'xxx},
lists: [{name:'手机',state:'0'},
{name:'computer',state='0'},
],
flag: true,
flag: false,
a1: 'a1',
a2: 'a2',
a3: 'a3',
a4:{color:'#000'},
a5:{color:'#f00'},
message: 'hello'
},
methods: {
add: function(name) {
if(name) {
this.lists.unshift({name:name, state:'0'});
this.text='';
}
},
del: (index) => {
this.lists.splice(index, 1); #删除数组中的1个值
},
},
filters: {
stateFilter: function(type) {
switch(type) {
case '0':
return '未采购';
case '1':
return '采购中';
case '2':
return '已采购';
default:
return '';
}
}
},
computed: { #计算属性
reversedMessage: function() {
return this.message.split('').reverse().join();
}
}
})
}
</script>
<style>
.a1 {
color: red;
font-size: 25px;
border: 1px solid #f60;
background: #f60;
}
.a2 {background: blue}
.a3 {color: blue;}
</style>
<h1 style="display: inline-block;">{{name}}购物清单列表</h1>
<input class="form-control" type="text" v-model="text"/>
<button class="btn btn-primary" v-on:click="add(name)">添加</buttion>
<tr v-for="(item, index) in lists">
<td>{{ item.name }}</td>
<td>{{ item.state | stateFilter }}</td>
</tr>
<ul>
<li v-for="(v, i) in arr">{{v}}--{{i}}</li>
</ul>
<ul>
<li v-for="(v, k) in obj">{{v}}--{{k}}</li>
</ul>
<ul>
<li v-for="(v, i) in lists">{{v}}--{{i}}--{{v.name}}</li>
</ul>
<button v-on:click="click1">click1</button>
<img v-bind:src="url" width="100" height="100" :title="title">
<img v-bind:src="flag?url:url2" width="100" height="100" :title="title">
<div :class="a1">123</div>
<div :class="[a1,a2]">345</div> #多个引用,后面会覆盖前面的
<div :class="{'a3':flag2}">567</div> #通过bool值判断样式
<div :style="{color: 'red',background: '#f60'}">789</div>
<div :style="a4">abc</div>
<div :style="[a4,a5]">def</div>
<div :class="flag?a1:a2">ghi</div> #三目运算
<div :style="flag? a4: a5">jkl</div>
MVC和MVVM
MVVM是前端视图层的分层开发思想,主要把每个页面分成了M|V|VM,VM是MVVM思想的核心也是M和V之间的调度者;
M,保存每个页面中单独的数据;
V,是每个页面中的HTML结构;
VM,是一个调度者分割了M和V,每当V层想要获取保存的数据时,都要由VM在中间做处理;
脚手架|开发环境安装:
安装nodejs; #插件Vue2 Snippets,Node Exec,HTML Snippets
安装vue的脚手架工具,官方命令行工具,npm install --global vue-cli,
vue init webpack vue-demo01 #创建项目,vue-router安装;ESLint代码检查工具(代码中多或少空格时会报错)选择No;e2e端到端测试;vue init webpack-simple vue-demo01更简单的目录结构且无语法检查
注:
http://npm.taobao.org/
npm install -g cnpm --registry=https://registry.npm.taobao.org #安装后就可用cnpm,npm默认用国外镜像,cnpm用taobao镜像
cd vue-demo01 #如果上步安装依赖不成功,再执行以下几行
cnpm install #安装package.json中定义的包
npm run dev #或npm start,启动服务,http://localhost:8080
另,3.0version安装:
npm install --global @vue/cli #
vue create my-project #过程中可设置自定义脚手架模板preset
vue add router #这种方式安装插件会整合到项目中
yarn serve
yarn global add @vue/cli-service-global
vue serve HelloWorld.vue #3ver可单独运行vue文件
vue ui #管理界面
设置全局变量:
.env #项目根下创建,.env.development开发环境(yarn serve),.env.production生产环境(yarn build)
VUE_APP_NAME=Test
src/HelloWorld.vue
{{ name }}
data() {
return {
name: process.env.VUE_APP_NAME,
}
}
npm install -g @vue/cli-init
npm install -g node-sass #vue init webpack-simple vue-demo01初始化时可选
npm run build #打包服务
vue.config.js # https://cli.vuejs.org/zh/config/#vue-config-js,3ver自定义配置(此例配置打包),再yarn build,在项目根下会生成build/,其下的index.html可直接打开
module.exports = {
baseUrl: './',
outputDir: 'build',
devServer: {
port: 5000,
proxy: { #跨域请求后端api
'/apis': {
target: 'http://localhost:3000',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/apis':''
}
}
}
}
}
注,html标签补全:
{
"emmet.triggerExpansionOnTab": true,
"files.associations": {
"*.js": "html",
"*.vue": "html"
}
}
{
"workbench.editor.closeEmptyGroups": false,
"emmet.triggerExpansionOnTab": true,
"files.associations": {
"*.js": "html",
"*.vue": "html"
}
}
nrm工具
nrm工具
只是单纯的提供了几个常用的下载包的url地址,能让我们在这几个地址间方便地切换,我们每次装包时用的装包工具都是npm;
npm i nrm -g
nrm ls
nrm use taobao
webpack
网页中常见的静态资源?
js #.js|.jsx,.coffee|.ts(TypeScript,类C#)是中间语言需转译为js
css #.css|.less|.sass|.scss,www.sass.hk
images #.jpg|.png|.gif|.bmp|.svg
fonts字体文件 #.svg|.ttf|.eot|.woff|.woff2
模板文件 #.ejs|.jade,.vue(这是在webpack中定义组件的方式,推荐这样用)
网页中引入的静态资源多了有?
网页加载速度慢,因为要发起很多二次请求;
要处理错宗复杂的依赖关系,如包和包之间有先后依赖;
如何解决上述2个问题?
对js|css,合并|压缩;
对图片,精灵图|base64编码;
可使用requireJS|webpack;
webpack,是基于整个项目构建的,是前端的一个项目构建工具,基于node.js开发出来的前端工具
gulp,是基于task的;
借助于webpack这个前端自动化构建工具,可完美实现资源的合并|打包|压缩|混淆等众多功能;
webpack.js.org
webpack.github.io
npm i webpack -g #全局安装,3.6.0
npm i webpack --save-dev #安装到项目依赖中
src/{js/,css/,images/}
dist/ #打包后的文件
package.json #npm init -y
webpack.config.js
ul>li*10{这是第$个li} #自动生成ul-li标签
js/{index.html,main.js}
import ... from ... #ES6中导入模块的方式,同node中const $=require('jquery'),新语法b解析不了,报syntaxError
>webpack ./src/main.js ./dist/bundle.js
src/index.html
<script src='../dist/bundle.js'></script>
webpack能处理js文件的互相依赖关系;
webpack能处理js的兼容问题,把b不识别的高级语法转为b能识别的语法;
webpack.config.js #这个配置文件其实就是一个js文件,通过node中的模块操作向外暴露了一个配置对象
const webpack = require('webpack') #启用热更新的第2步
module.exports = {
entry: path.join(__dirname, './src/main.js'), #入口,表示要使用webpack打包的那个文件
output: { #输出文件相关的配置,指定打包好的文件输出到哪个目录中去
path: path.join(__dirname, './dist'),
filename: 'bundle.js'
},
devServer: {
open: true, #自动打开b
port: 3000,
contentBase: 'src', #指定源码路径
hot: true #启用热更新第1步
},
plugins: [ #配置插件的节点,webpack中只要带s的都是数组,与vue相反(vue中带s的是对象,props是数组)
new webpack.HotModuleReplacementPlugin() #启用热更新第3步,new一个热更新的模块对象
]
}
>webpack #直接运行打包命令
当在命令行输入webpack时,做了如下几步:
webpack发现没有在命令行输入入口和出口;
去项目的根目录中,找webpack.config.js,并解析配置,打包构建;
webpack-dev-server,实现自动打包编译功能;
npm i webpack -D #即使全局安装了,为保证webpack-dev-server正常运行,还要在本地安装
npm i webpack-dev-server -D #安装至本地开发依赖
npm run dev
该工具打包生成的bundle.js在内存中,并没存放到物理磁盘上,所以在项目根目录中找不到打包好的bundle.js;可认为该工具打包好的文件以一种虚拟的形式托管到了项目的根目录中,和dist|src|node_modules平级,有一个看不见的文件是bundle.js
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --port 3000 --contentBase src --hot", #方式1
//"dev2": "webpack-dev-server", #方式2,还要在webpack.config.js中配置
},
cnpm i html-webpack-plugin #将静态文件也放至内存中,当前仅bundle.js在内存中
使用这个插件后,我们不再需要手动处理bundle.js的引用路径了,因为这个插件已经帮我们自动创建了一个合适的script,并且引用了正确的路径;
该插件的2个作用:自动在内存中根据指定页面生成一个内存的页面;自动把打包好的bundle.js追加到页面中;
webpack.config.js
const htmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new htmlWebpackPlugin({
template: path.join(__dirname, './src/index.html'),
filename: 'index.html',
})
]
导入样式:
main.js
import './css/index.css' //webpack默认只能打包处理js文件,无法处理其它的非js类型的文件,要用第三方loader加载器
import './css/index.less'
import './css/index.scss'
cnpm i style-loader css-loader less less-loader node-sass sass-loader -D
webpack.config.js #新增一个配置节点module,它是一个对象,在这个module上,有个rules属性,这个rules属性是个数组,这个数组中存放了所有第三方文件的匹配和处理规则
module: { //这个节点用于配置所有第三方模块加载器
rules: [ //所有第三方模块的匹配规则
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, //从右至左调用处理,后一个先处理,再交给前一个处理;webpack2ver开始配置时要加上-loader,1ver可省
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
]
}
webpack处理第三方件类型的过程:
发现这个要处理的文件不是js文件,去配置文件中查找有没有对应的第三方loader规则
如果能找到对应的规则,就会调用对应的loader处理这种文件类型;
在调用loader时,是从后往前调用的;
当最后一个loader调用完毕,会把处理的结果直接交给webpack进行打包合并,最终输出到bundle.js中;
默认webpack无法处理css文件中的url,不管是图片还是字体库;
cnpm i url-loader file-loader
webpack.config.js
module: {
rules: [
{test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=7632'},
]
}
//limit给定的值(单位byte)是图片的大小,若引用的图片>=limit值则不会被转为base64格式的字符串,若图片大小<limit值则会转为base64字符串,使用base64是防止图片名重复
use: 'url-loader?limit=7631&name=[name].[ext]',传参方式,加name=[name].[ext]表示按原文件名输出,而不显示base64字串,如果2图片名相同,则后一个会覆盖前一个
use: 'url-loader?limit=7631&[hash:8]-[name].[ext]',解决后一个覆盖前一个
模块化:
import|export
export default #导出一个默认对象,对应的导入export xx from xx
export #导出一个对象,对应的导入import {xx} from xx
src/router/index.js中import HelloWorld from '@/components/HelloWorld' #@表示根路径,在build/webpack.base.conf.js中有配置
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
Vue.use(Router) #使用1个全局的组件
<style scoped> #scoped仅在当前作用域有效
目录结构:
node_modules/
src/ #开发用到的文件,src/assets/静态资源,src/App.vue根组件(template段html代码(vue的模板里面,所有的内容要被一个根节点包含起来),script段为业务逻辑代码,style段),src/main.js配置路由时用
.babelrc #babel配置文件
.editorconfig #编辑器配置文件
index.html #入口文件
package.json #项目配置文件
webpack.config.js #webpack的配置文件,webpack是模块化打包工具,可把.vue文件打包为b可解析的
packages.json #dependencies需打包发布到生产,devDependencies开发依赖
src/main.js #入口文件
例:
绑定数据;
绑定对象;
循环数组渲染数据;
src/App.vue
<template>
<div id="app">
<h2>{{msg}}</h2>
<br>
这是一个根组件
<br>
<h3>{{obj.name}}</h3>
<br>
<ul>
<li v-for="item in lst">{{item}}</li>
</ul>
<br>
<ul>
<li v-for="item in lst">
{{item.cate}}
<ol>
<li v-for="news in item.lst">
{{news.title}}
</li>
</ol>
</li>
</ul>
</div>
</template>
<script>
export default {
data() { #业务逻辑里面的数据
return {
msg: '你好vue',
obj: {name:'zhangsan'},
lst: [1,2,3],
lst1:[
{'title':111},
{'title:222}
],
lst2:[
{
"cate":"国内新闻“,
”lst":[
{"title":"国内新闻111"},
{"title":"国内新闻222”}
]
},
{
"cate":"国际新闻“,
”lst":[
{"title":"国际新闻111"},
{"title":"国际新闻222”}
]
}
]
}
}
}
</script>
例:
插值表达式|绑定数据|绑定属性|绑定html|绑定class|绑定style
v-cloak|v-text|v-html|v-bind|v-on|v-model|v-for|v-if|v-show;
插值表达式{{msg}};
vue中,
使用事件绑定机制,为元素指定处理函数时,没有参数可不加(),如果加了(),就可给函数传参了;
使用v-model数据的双向绑定,每当我们修改了data中的数据,vue会默认监听到数据的改动,自动把最新的数据应用到页面上;当我们意识到这点,就证明已入门vue了,我们更多的是在进行VM中model数据的操作,同时在操作model数据时,进行业务逻辑操作;
<p v-cloak>+++{{ msg }}-----</p>,解决插值表达式的闪烁问题;
<h4 v-text="msg">=======</h4>,默认是没有闪烁问题的;
v-text和v-html会覆盖元素中原本的内容,但插值表达式只会替换占位符,不会把整个内容清空;
<div v-html="msg2"></div>
<input type="button" value="按钮" v-bind:title="mytitle + '123'">,v-bind可简写为:,v-bind中可写合法的js表达式;
<input type="button" value="按钮" v-bind:title="mytitle + '123'" v-on:click="show">,v-on事件绑定机制,缩写@,click|mouseover;
v-model
唯一一个实现双向数据绑定,实现表单元素和model中数据双向绑定,只能运用在表单元素中,如input(radio|text|address|email...),select|checkbox|textarea;
v-model只能应用于表单元素;
<input type="text" style="width:100%;" v-model="msg">
v-bind只能实现数据的单向绑定,从M自动绑定到V;
<input type="text" v-bind:value="msg" style="width:100%;">
var codeStr = 'parseInt(this.n1)' + this.opt + 'parseInt(this.n2)'
this.result = eval(codeStr) #投机取巧方式,生产中少用,使用swith...case语句
使用样式:
在vue中绑定样式,v-bind:class,v-bind:style;
原始方式:
<h1 class="thin italic">这是一个很大很大的h1</h1>
方式1:传递数组,此处class要用v-bind绑定
<h1 :class="['thin','italic']">这是一个很大很大的h1</h1> #thin和italic在<style>中定义
<h1 :class="['thin','italic',flag?'active':'']">这是一个很大很大的h1</h1> #在数组中使用三元表达式,flag在data中定义
<h1 :class="['thin','italic',{‘active':flag}]">这是一个很大很大的h1</h1> #三元表达式改为对象形式,提高可读性
方式2:使用对象,在为class使用v-bind绑定对象的时候,对象的属性是类名,可带引号也可不带,属性的值是一个标识符
<h1 :class='{red:true,thin:true,italic:false,active:false}'>这是一个很大的h1</h1>
使用内联样式:
对象就是无序键值对的集合
<h1 :style='{color:red,"font-weight"=200}'>这是一个H1</h1> #属性名中有-要用引号引起来
<h1 :style='[styleObj1, styleObj2]'>这是一个h1</h1> #data中定义2个对象,用数组包起来
v-for和key
在v-for中要用key属性,只接受string|number类型;
<p v-for='item in lst'>{{item}}</p>
<p v-for='(item,k) in lst'>{{k}}--{{item}}</p>
<p v-for='user in lst'>{{user.id}}--{{user.name}}</p> #对象数组lst:[{id:1,name:'tst'},id:2,name:'tst2}]
<p v-for='(val,k,index) in user'>{{val}}--{{k}}--{{index}}</p> #对象,data中user为对象user:{id:1,name:'tst',gender:'man'}
<p v-for='count in 10'>这是第{{count}}</p> #数字,迭代数字起始数为1
使用key注意:
v2.20+中,在组件中使用v-for时,key是必须的;
v-for中key,key属性只能使用number或string类型,不能用object;
key必须使用v-bind属性绑定的形式;
<p v-for="item in lst" :key="item.id">{{item.id}}--{{item.name}} #lst: [{id:1,name:'tst'},id:2,name:'tst2}]
v-if和v-show
v-if,每次都会重新删除或创建元素,有较高的切换性能消耗;
v-show,每次不会重新进行dom的删除和创建操作,只是切换了元素的display: none样式,有较高的初始渲染消耗;
如果元素涉及到频繁的切换,不要用v-if,推荐用v-show;
如果元素永远不会被显示出来推荐用v-if;
<input type="button" value="toggle" @click="flag=!flag">
<h3 v-if="flag">这是v-if</h3>
<h3 v-show='flag'>这是v-show</h3>
data: { flag: true }
<img v-bind:src='url'
<img v-bind:src='flag?url:url2' #三目运算
<div :class='a1' #单个引用
<div :class='[a1,a2]' #多个引用,后面的会覆盖前面的
<div :class='{'a3':flag}' #通过bool值判断
<div :style='{color:"red",background:"#ffff"}'
<div :style='a4'
<div :style='[a4,a5]'
<div v-text='msg'></div> #输出文本
<div v-html='html'></div> #可解析'html':'<p>test</p>'
例:
src/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{ msg }}</h1>
<div v-text="msg"></div>
<div v-bind:title="title">鼠标放上去看一下</div>
<br>
<img v-bind:src="url" alt="">
<div v-html="h"></div>
<ul>
<li v-for="item in lst">
{{item}}
</li>
</ul>
<ul>
<li v-for="(item,key) in lst" :class="{red:key==0,blue:key==1}">
{{key}}-{{item}}
</li>
</ul>
<div v-bind:class="{'red':flag}">我是一个div</div>
<br>
<div :class="{'red':flag,'blue':!flag}">我是一个div</div>
<div class="box" v-bind:style="{width:boxWidth+'px'}">test</div>
</div>
</template>
<script>
export default {
// name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App',
title: '我是一个title',
url: 'http://localhost:8080/dist/logo.png',
h: '<h2>我是h2</h2>',
lst: ['1','2','3'],
flag: false,
boxWidth:300
}
}
}
</script>
<style lang="scss">
.red{
color: red;
}
.blue{
color: blue;
}
.box{
width: 100px;
height: 100px;
background: red;
}
</style>
例:
双向数据绑定,v-model,借鉴了angular1,MVVM,module view view module,model改变会影响view,view改变反过来影响model;
双向数据绑定必须在表单里使用;
事件介绍;
vue中ref获取dom节点;
v-model.lazy,v-model.number,v-model.trim;
<input type='text' v-model='age' :disabled='true'
<input type='checkbox' v-model='che' #che='false',单个复选框
<input type='checkbox' v-model='arr' value='0'/>老师 #arr:[],多个复选框
<input type='checkbox' v-model='arr' value='1'/>学生
<input type='checkbox' v-model='arr' value='2'/>家长
<input type='radio' v-model='radioV' name='test' value='0'/>男 #radioV:''
<input type='radio' v-model='radioV' name='test' value='1'/>女
{{radioV}}
<select v-model='selected'> #items:[{text:'sing',value:'0'},{text:'movie',value:'1'},{text:'dance',value:'2'}]
<option v-for='(v,k) in items' :value='v.value'>{{v.text}}</otpion>
</select>
{{selected}}
src/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{ msg }}</h1>
<input type="text" v-model='msg'>
获取表单数据</button>
设置表单数据</button>
<br>
<input type="text" ref="userinfo">
我是一个box</div>
获取第二个表单中的数据</button>
</div>
</template>
<script>
export default {
// name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
methods: {
getMsg() {
方法执行');
alert(this.msg)
},
setMsg() {
我是改变后的数据';
},
getInputValue() {
console.log(this.$refs.userinfo);获取ref定义的dom节点
this.$refs.box.style.background='red';
alert(this.$refs.userinfo.value);
}
}
}
</script>
<style>
</style>
例:
事件-定义方法;
事件-执行方法;
事件-获取数据;
事件-改变数据;
事件-执行方法传值;
事件对象;
注:
阻止默认行为,a标签,click后默认行为为href,e.preventDefault
阻止事件传播,内-->外,外-->内,e.stopPropagation
@click='run1()' #同v-on:click='run1()',能执行方法和表达式
@keydown.13='enter()' #@keydown.enter='enter($event)',键盘按下事件,另@keydown.a,@keydown.esc,@keydown.left,@keydown.alt,@keydown.alt.65(组合键alt+a)
@mouseenter=
@mouseleave=
@keyup.enter=
@keydown.enter=
v-show
<div @click.stop='show()' class='c'>ccccc</div #阻止事件冒泡
<div href='http://www.baidu.com' @click.prevent='show()'>click me</div> #阻止默认行为,即使将href=''或href='#'都不可以,还是会刷新整个页面,对于单页面应用,元素过多要刷新整个页面是难以想象的
<button @click.once='show()'>once</button> #只执行1次
事件修饰符,.stop|.prevent|.capture|.self|.once;
事件修饰符:
.stop #阻止冒泡,从内到外
.prevent #阻止默认事件,如<a>中跳转
.capture #添加事件侦听器时使用事件捕获模式,从外到内
.self #只当事件在该元素本身触发时触发回调;
.once #事件只触发一次
事件修饰符:
.stop,阻止冒泡;
<div class="inner" @click="div1Handler">
<input type="button" value="戳它“ @click.stop="btnHandler">
</div>
.prevent,阻止默认事件;
<a href="http://www.baidu.com" @click.prevent="linkClick">有问题找它</a>
.capture,捕获触发事件的机制;
<div class="inner" @click.capture="div1Handler">
<input type="button" value="戳它“ @click="btnHandler">
</div>
.self,点击当前元素时,才触发事件处理函数;只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡的行为;
<div class="inner" @click.self="div1Handler">
<input type="button" value="戳它“ @click="btnHandler">
</div>
.once,只触发1次事件处理函数,@click.prevent.once和@click.once.prevent无先后之分,效果一样;
<a href="http://www.baidu.com" @click.prevent.notallow="linkClick">有问题找它</a>
键盘事件修饰符:
vue允许为v-on在监听键盘事件时添加修饰符;
<input @keyup.enter='submit'>
vue提供部分按键别名,其它的没提供得用数字代替(查js键盘事件对应的键码):
.enter #或.13
.tab
.delete #捕获删除和退格
.esc
.space
.up
.down
.left
.right
.113 #F2
自定义全局按键修饰符:
Vue.config.keyCodes.f2=113 #@keyup.f2='add'
.native #仅使用vue组件时用到此修饰符,使用原型绑定
src/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{ msg }}</h1>
执行方法方式1</button>
<br>
执行方法方式2</button>
<br>
获取data里的msg</button>
<br>
改data里的msg</button>
<br>
请求数据</button>
<ul>
<li v-for="(item,key) in lst">
{{key}}--{{item}}
</li>
</ul>
<br>
执行方法传值</button>
<br>
事件对象</button>
</div>
</template>
<script>
export default {
// name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App',
lst: [],
}
},
methods: {
run1: function() {
方法1');
},
run2() {
方法2')
},
getMsg() {
alert(this.msg);
},
setMsg() {
我是改变后的数据';
},
requestData() {
for(let i=0;i<5;i++) {
this.lst.push('i am '+i)
};
},
deleteData(val) {
alert(val);
},
eventFn(e) {
console.log(e);
e.srcElement.style.background='red';
console.log(e.srcElement.dataset.aid);
}
}
}
</script>
<style>
</style>
watch|computed|methods区别:
methods|computed|watch:
computed必须有返回值,而watch中不必有返回值;
methods中放业务逻辑;
例,computed应用:
例,完整名的拼接:
方式1:通过键盘事件@keyup监听;
方式2:
watch属性,可监视data中指定数据的变化,然后触发watch中对应的方法;
newVal为当前input框中输入的值,oldVal为改变前一刻的值;
通过watch监听路由的改变,watch可监听非DOM元素的改变;
方式3:
computed属性中,可定义一些属性,这些属性叫计算属性,本质就是1个方法,只不过我们在使用这些计算属性时,是把它们的名称直接当作属性来使用,并不会把计算属性当作方法调用;
注意:
计算属性在引用时,一定不能加(),直接把它当作普通属性使用;
只要计算属性这个方法内部,所用到的任何数据发生了变化,就会立即重新计算这个计算属性的值;
计算属性的求值结果,会被缓存起来,方便下次直接调用,如果计算属性方法中依赖的任何数据都没发生变化,则不会对计算属性重新求值;
注:fullname不在data中定义,在computed中定义;
自定义指令:
vue中所有指令都以v-开头;
Vue.directive('focus',{
bind: (el)=>{},
inserted: (el)=>{el.focus()},
updated: (el)=>{}
})
#DIRECTIVE_NAME定义时不需加v-前缀,在调用时才加;参数2是一对象,指令相关的函数,这些函数可在特定的阶段执行相关的操作
el,参数1,指令所绑定的元素,可用来直接操作DOM
binding,参数2,注意value和expression
bind #每当指令绑定到元素上时,会立即执行这个bind函数,只执行1次,和样式相关的在此处设置
inserted #元素插入到DOM中时,会执行,触发1次,和js行为有关的操作,在此处设置,防止js行为不生效
updated #当VNode更新时,会执行,可能会触发多次
这3个函数,第1个参数永远是el,表示被绑定了指令的那个元素,这个el参数是一个原生的js对象;
在元素刚绑定了指令时,还没有插入到DOM中去,这时调用el.DIRECTIVE_NAME(el.focus)没作用,因为一个元素只有插入DOM后,才能获取焦点;
<input type="text" class="form-control" v-model="keywords" id="search" v-focus> #使用
自定义一个设置字体颜色的指令
样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式,将来元素肯定会显示到页面中,这时b的渲染引擎必然会解析样式,应用给这个元素;
和样式有关的操作,一般都可在bind中执行;
Vue.directive('color' {
bind: el => {
el.style.color = 'red';
}
})
<input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color>
Vue.directive('color' { #全局定义
bind: (el, binding) => {
//el.style.color = 'red';
console.log(binding.name);
console.log(binding.value);
console.log(binding.expression);
el.style.color = binding.value;
}
})
<input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color="'blue'"> #注意双引号里有单引号
methods: {},
filters: {},
directives: {
'fontweight': {
bind: (el, binding) => {
el.style.fintWeight = binding.value;
}
}
}
<h3 v-color="'pink'" v-fnotallow="900">{{dt|dateFormat}}</h3> #设置字体粗细
简写形式,会在bind和updated上生效
directives: {
'fontsize': (el,binding) => {
el.style.fontsize = parseInt(binding.value;) + 'px';
}
}
<h3 v-color="'pink'" v-fnotallow="900" v-fnotallow="'30px'">{{}}</h3>
过滤器:
用作文本格式化;
全局定义|私有定义,过滤器调用时是就近原则,全局和私有都定义了,优先使用私有的;
用在2个地方:mustache插值表达式和v-bind;
可用多个过滤器,多次处理;
在html中编写时,全局定义:
<script>
Vue.filter('FILTER_NAME', ()=>{})
</script>
例1:
<template>
<div>
{{number | addZero}}
<br>
{{fnumber | fixTwo(2)}}
<br>
{{timeData | dateFormat}}
</div>
</template>
<script>
import store from '../vuex/store.js';
export default {
data() {
return {
msg: 'i am home.',
lst: [
{text:'sing',value:'0'},
{text:'movie',value:'1'},
{text:'dance',value:'2'}
],
selected: '0',
number: 3,
fnumber: 3.1415926,
取前10位
}
},
store,
methods: {
incCount() {
// this.$store.commit('incCount');
this.$store.dispatch('incMutationsCount');
}
},
filters: {
addZero(num) {
if(num<10) {
return '0' + num;
}
},
fixTwo(num, n) {
return num.toFixed(n);
},
dateFormat(timestamp) {
let d = new Date(timestamp*1000);
return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate(); ##d.getDay(),d.getHours(),d.getMinutes(),d.getSeconds()
}
}
}
</script>
<style>
</style>
localStorage
webstorage:本地存储,存储在客户端,包括localStorage|sessionStorage;
localStorage:生命周期是永久,即用户显式地清除否则信息将永久存在;存放数据大小一般为5M,而且仅在b中保存,不参与和server的通信;
localStorage.setItem('key','value');
localStorage.getItem('key');
localStorage.removeItem('key');
localStorage.clear();
例:
todolist,进行中|已完成;
事件 + 双向数据绑定 + localStorage实现;
@click;
@change,文本框改变-事件,对应HTML事件onchange,事件监听要比watch监听效率高(单页面中超过2000个watch影响性能);
mounted(),生命周期函数,vue页面刷新时会触发的方法,和methods|data是一级;
src/model/storage.js #模块化封装localStorage;
src/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{ msg }}</h1>
<input type="text" v-model='todo' @keydown="doAdd($event)">
<!-- <button @click='doAdd()'>Add</button> -->
进行中</h2>
<ul>
<li v-for="(item,key) in lst" v-if='!item.checked'>
<input type="checkbox" v-model='item.checked' @change='saveList()'>{{item.title}}---<button @click="removeData(key)">Delete</button>
</li>
</ul>
已完成</h2>
<ul>
<li v-for="(item,key) in lst" v-if='item.checked'>
<input type="checkbox" v-model='item.checked' @change='saveList()'>{{item.title}}---<button @click="removeData(key)">Delete</button>
</li>
</ul>
</div>
</template>
<script>
import storage from './model/storage.js';
export default {
// name: 'app',
data () {
return {
msg: 'Welcome to Your Vue.js App',
todo: '',
lst: []
}
},
methods: {
doAdd(e) {
// console.log(e)
为13
if(e.keyCode==13){
this.lst.push({
title: this.todo,
checked: false
});
this.todo = '';
// localStorage.setItem('lst', JSON.stringify(this.lst));
storage.set('lst', this.lst);
}
},
removeData(key) {
this.lst.splice(key,1); #删除数组中的1个值,这种方式不好,改为this.lst = this.lst.filter(v => v.key===key),另localStorage.clear()|localStorage.removeItem('xxx')
// localStorage.setItem('lst', JSON.stringify(this.lst));
storage.set('lst', this.lst);
},
saveList() {
// localStorage.setItem('lst', JSON.stringify(this.lst));
storage.set('lst', this.lst);
},
},
mounted() {
// let lst = JSON.parse(localStorage.getItem('lst'));
var lst = storage.get('lst');
if(lst) {
this.lst = lst;
}
}
}
</script>
<style>
</style>
组件
组件:
为拆分vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可去调用对应的组件;
组件化和模块化的不同:
模块化是从代码逻辑的角度进行划分的,方便代码分层开发,保证每个功能模块的职能单一;
组件化是从ui界面的角度划分的,前端的组件化,方便ui组件的重用;
1个组件中包含html|js|css,即<template>|<script>|<style>,1个组件就是1个页面;
组件里的template必须有1个根节点<div></div>;
src/components/Home.vue #组件文件名首字母大写;
src/App.vue #在根组件中,1引入组件Home,2挂载组件(命名规范,用连接符-),3在模板中使用;
<style lang="scss" scoped> #scoped为css局部作用域
嵌套关系,如<table>与<tr>|<td>,<ul>与<li>;
组件插槽slot;
动态组件,创建|销毁;
保持组件状态,<keep-alive></keep-alive>,缓存非活动组件;
<table>
<tr is="my-tr"></tr> #is表示里面要是个组件
</table>
<button @click="flag='mytab'">my-tab</button>
<button @click="flag='my-address'">my-address</button>
<keep-alive>
<component :is="flag"></component> #:is表示data(){}中绑定的变量
</keep-alive>
创建组件:
<mycomponent1></mycomponent1> //定义时可用驼峰也可都小写,使用时都小写
<script>
var com1 = Vue.extend({ //定义方式1
template: '<div><h3>...</h3></div>' //template属性指定的模板内容,必须有且只能有唯一的根元素
}
Vue.component('myComponent1', com1) //全局,定义时可用驼峰也可都小写,使用时都小写
</script>
var login = { //定义方式2
template: '<div><h3>...</h3></div>'
}
Vue.component('mycom2', login)
或
Vue.component('mycom2', { //定义方式2
template: '<div><h3>...</h3></div>'
})
<body>
<div id='app'>
<mycom3></mycom3>
</div>
<template id="tmp1"> #在被控制的#app外面,定义组件的html结构
<div>
<h1>...</h1>
</div>
</template>
<script>
Vue.component('mycom3', { //定义方式3
template: '#tmp1'
})
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
</script>
</body>
var vm = new Vue({
el: '#app',
data: {},
methods: {},
filters: {},
directives: {},
components: {}, //定义实例内部私有组件
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdated() {},
updated() {},
beforeDestroy() {},
destroyed() {}
})
components: { //定义私有组件,方式1
login: {
template: '<div>...</div>' //或template: '#tmp2'
}
}
或
var login = { //方式2
template: '<div><h3>...</h3></div>'
}
var vm = new Vue({
...,
components: {
login
}
})
组件中的data和methods:
组件可以有自己的data,和实例的data不一样,实例中的data可以为一个对象,但组件中的data必须是一个方法,且这个方法内部必须返回一个对象;
//var dataObj = {count:0}
Vue.component('counter', {
template: '#tmp1',
data() {
//return dataObj; //当多次引用这个组件时会有影响
return {
count: 0;
}
},
methods: {
increment() {
this.count += 1
}
}
})
组件切换:
方式1,用flag,缺陷布尔值只有2个:
<div id='app'>
<a href='' @click.prevent='flag=true'>login</a>
<a href='' @click.prevent='flag=false'>registe</a>
<login v-if='flag'></login>
<registe v-else='flag'></registe>
</div>
<script>
Vue.component('login', {
template: '<h3>login</h3>
})
Vue.component('registe', {
template: '<h3>registe</h3>
})
var vm = new Vue({
el: '#app',
data: {
flag: true,
},
})
</script>
组件切换,方式2:
<div id='app'>
<a href='' @click.prevent='comName="login"'>login</a>
<a href='' @click.prevent='comName="registe"'>registe</a>
<component :is='comName'></component> //vue提供了component来展示对应名称的组件,component是一个占位符,:is属性可用来指定要展示的组件的名称,注意:is中的名称"comName"和"'comName'"区别
</div>
<script>
Vue.component('login', {
template: '<h3>login</h3>
})
Vue.component('registe', {
template: '<h3>registe</h3>
})
var vm = new Vue({
el: '#app',
data: {
comName: 'login', //当前component中:is绑定的组件名称
},
})
</script>
总结:
vue提供的标签:
component
template
transition
transitionGroup
props #唯一一个是数组
router-view #VueRouter提供
router-link
组件切换动画;
父组件向子组件传值:
子组件中默认无法访问到父组件中data中的数据和methods中的方法;
在父组件中,可以在引用子组件时通过v-bind:形式,把需要传递给子组件的数据,以属性绑定形式传递到子组件内部,供子组件使用,如<com1 :parentmsg='msg'></com1>;
在子组件中,定义属性props: ['parentmsg'],props属性是数组,组件里所有props中数据,都是通过父组件传递给子组件的,子组件中使用时{{parentmsg}};
子组件中的data数据,是自己私有的,如通过ajax请求来的数据可放到data中;
props中的数据,都是只读的;
data中的数据,都是rw;
父组件向子组件传方法:
使用事件绑定机制;
父组件上,<com2 @func='show'></com2>;
子组件上,
<template id='tmp1'>
<div>
<h1>...</h1>
<input type='button' value='子组件' @click='myclick'>
</div>
</template>
<script>
var com2 = {
template: '#tmp1',
methods: {
myclick() {
this.$emit('func'); //emit触发;如果父组件中的show带参数,此处为this.$emit('func', PARA1, PARA2),变相地子组件向组件传值
}
}
}
</script>
ref获取DOM元素:
<h3 id='myh3' ref='myh3'>...</h3>
methods: {
getElement() {
//console.log(document.getElementById('myh3').innerText) //jquery方式获取DOM元素中内容
console.log(this.$refs.innerText);
},
}
ref获取组件:
<login ref='mylogin'></login>
console.log(this.$refs.mylogin.show()) //this.$refs.mylogin.msg
Math.random() #随机数
src/components/Header.vue
src/components/News.vue
src/components/Home.vue
<template>
<div>
<v-header></v-header> //3在模板中使用
<br>
<h1>{{msg}}</h1>
执行run方法</button>
</div>
</template>
<script>
import Header from './Header.vue'; //1引入组件
export default {
data() {
return {
我是一个首页组件'
}
},
methods: {
run() {
alert(this.msg);
}
},
components: {
'v-header': Header //2挂载组件
}
}
</script>
<style lang="scss" scoped>
h1{
color:red;
}
</style>
src/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{ msg }}</h1>
<v-header></v-header>
<hr>
<v-home></v-home>
<hr>
<v-news></v-news>
<hr>
</div>
</template>
<script>
import Header from './components/Header.vue';
import Home from './components/Home.vue';
import News from './components/News.vue';
export default {
// name: 'app',
data() {
return {
msg: 'Welcome to Your Vue.js App',
}
},
components: {
'v-header': Header,
'v-home': Home,
'v-news': News,
},
methods: {
},
}
</script>
<style>
</style>
例:
父子组件传值-父组件给子组件传值
父组件给子组件传值:
1父组件调用子组件时,绑定动态属性,可将父组件中的值|方法|整个父组件this传给子组件;
2在子组件里通过props: ['homemsg',]接收父组件传的值,也可通过props: {'homemsg':String}来验证合法性;
3直接在子组件中使用父组件中的值;
src/components/Home.vue
<template>
<div>
<v-header :title='title' :homemsg='msg' :run='run' :home='this'></v-header>
<br>
<h1>{{msg}}</h1>
</div>
</template>
<script>
import Header from './Header.vue';
export default {
data() {
return {
我是一个首页组件',
首页Home'
}
},
methods: {
run(data) {
我是Home组件的run方法'+data);
}
},
components: {
'v-header': Header,
}
}
</script>
<style lang="scss" scoped>
h1{
color:red;
}
</style>
src/components/Header.vue
<template>
<div class="header">
<h3>{{msg}}--{{title}}--{{homemsg}}</h3>
<br>
<button @click='run(123)'>执行父组件的方法</button>
<br>
获取父组件的数据和方法</button>
</div>
</template>
<script>
export default {
data() {
return {
我是一个头部组件'
}
},
methods: {
getParent() {
alert(this.title);
alert(this.home.title);
this.home.run('456');
}
},
props: ['title', 'homemsg', 'run', 'home']
}
</script>
<style lang="scss" scoped>
.header {
background: #000;
color: #fff;
}
</style>
例:
父子组件传值-父组件主动获取子组件的数据和方法
1父组件里,在调用子组件时定义一个ref,<v-header ref="header"></v-header>
2父组件里,通过this.$refs.header.属性,this.$refs.header.方法;
父子组件传值-子组件主动获取父组件的属性和方法
1父组件里,在调用子组件时定义一个ref,<v-header ref="header"></v-header>
2子组件里,this.$parent.属性,this.$parent.方法;
src/components/Home.vue
<template>
<div>
<h3>{{msg}}</h3>
<v-header ref='header'></v-header>
<button @click='getChildData()'>父组件主动获取子组件的属性和方法</button>
</div>
</template>
<script>
import Header from './Header.vue';
export default {
data() {
return {
msg: 'i am vue framework.'
}
},
methods: {
run() {
我是父组件Home的run方法');
},
getChildData() {
// alert(this.$refs.header.msg);
this.$refs.header.run();
}
},
components: {
'v-header': Header
}
}
</script>
<style>
</style>
src/components/Header.vue
<template>
<div>
<h3>{{msg}}</h3>
<button @click='getParentData()'>子组件主动获取父组件的属性和方法</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'i am a header.'
}
},
methods: {
run() {
我是子组件的run()')
},
getParentData() {
// alert(this.$parent.msg);
this.$parent.run();
}
}
}
</script>
<style>
</style>
例:
非父子组件传值
1新建1个js文件VueEvent.js,导入Vue,实例化Vue,暴露这个实例;
2在要广播的地方导入定义的实例,import VueEvent from './model/VueEvent.js';
3通过VueEvent.$emit('NAME',value)
4在接收数据的地方,在mounted()中定义VueEvent.$on('NAME',function(data) {console.log(data);});
src/model/VueEvent.js
import Vue from 'vue';
let VueEvent=new Vue();
export default VueEvent;
src/components/Home.vue
<template>
<div>
<h3>{{msg}}</h3>
<button @click='emitNews()'>给News组件广播数据</button>
</div>
</template>
<script>
import VueEvent from '../model/VueEvent.js';
export default {
data() {
return {
msg: 'i am home.',
}
},
methods: {
emitNews() {
VueEvent.$emit('to-News', this.msg);
},
},
mounted() {
VueEvent.$on('to-News', function(data) {
console.log(data);
})
}
}
</script>
<style>
</style>
src/components/News.vue
<template>
<div>
<h3>{{msg}}</h3>
<button @click='emitHome()'>给Home组件广播数据</button>
</div>
</template>
<script>
import VueEvent from '../model/VueEvent.js';
export default {
data() {
return {
msg: 'i am News.',
}
},
methods: {
emitHome() {
VueEvent.$emit('to-Home', this.msg);
},
},
mounted() {
VueEvent.$on('to-Home', function(data){
console.log(data);
})
}
}
</script>
<style>
</style>