Vue笔记
和元素JS的区别,元素JS的编程范式是命令式编程,一步一步来的。
vue的编程范式:声明式编程,可以做到数据和页面完全分离react/vue
react/vue 都是响应式编程,只要操作了数据,页面就会发生变化。
什么是高阶函数? 就是这个函数里面的参数也包括函数 比如 map/filter/reduce
let 有块级作用域 ,var没有,在if中声明后,if外面也可以用
闭包为什么可以解决问题?因为函数是一个作用域,而for if都不是
优先使用const 避免被同事修改
{}是对象的自变量
请求数据正确的逻辑应该是,单独写一个接口.js/ts文件,把接口得到的数据都return出去,home.vue是面向home.js/ts 开发
import {request} form 'xxx'; export function getHomeMultidata() { return request({ url: '/home/multidata' }) }
只有default export 才能 import a from
下面这种只能import {getData} from 'xxx'
function getData(x, y) { return ...; }; export {getData};
V8引擎 直接把JS转成二进制
async是把异步操作变为同步操作
对象在内存中都是用内存地址保存的
0×00001
为什么可以修改const声明对象中的属性呢?因为只是改内部的属性,他的地址没有变,还是住在那个地方。
计算属性 有缓存,每次都会检查变量值有没有变化,如果变化了才会调用,性能更高。
一般处理数据变化用计算属性。特殊处理用methods
计算属性,这样写其实是简写,只用了get方法
显示数据的三种方式
1.直接拼接 2.通过定义methods 3.通过computed
简单情况使用v-if v-else 复杂情况使用computed
为什么切换input框后,刚刚填写的值还在 没有为空?
因为: vue底层做了处理。把虚拟dom放到内存中,如果判断为一样的,就会复用,要解决这个问题,就得往标签上添加 key标识
key是为了高效的更新虚拟DOM
为什么不用index?因为index在变化的列表中没有意义,key是标识符,是为了标记这个元素,所以一定要是一一对应的,但是这个index它是可以变化的,比如说你往中间插入一个元素,那么这个元素后面元素的index都会变化,所以不能用index
响应式方法
unshift
shift
pop
push
splice(开始位置,删除的元素个数,新增的元素1,新增2,新增3...) 如果第二个参数不传,就把开始位置后面的都删除了。可以
过滤器
{{item.price | showPrice}}
优势
可以写多个,而函数不方便
有局部过滤器,也有全局过滤器
在vue中
数组和对象都是用v-for遍历
const app = new Vue(){ el:'#app', //用于挂载要管理的元素 date: { message:123 } //定义数据 })
可以直接用(因为div已经交给new Vue管理了,一旦交给他管理,那么它就会对它管理的内容进行解析)
Mustache语法(双大括号)
<div id="app">{{message}}</div>
事件监听都是v-on开头
例如: v-on:click 等价于 react的onClick
MVVM
DOM页面 Vue实例 数据
Model(数据)想要和Viewl通信(想要驱动View),就要通过Vue实例 (首先绑定数据,然后利用DOM Listeners进行事件监听来回调监听的东西)
生命周期:
源码
beforeCreate,created 都是钩子函数, 他们可以勾住方法。
created 一般用来发送网络请求
date是函数 返回一个实例对象,对象内保存着数据
data(){ return { isActive:true, selectIndex:this.defaultIndex } }
指令
- v-once 只渲染一次,改变数据不会变化
- v-html 解析标签
- v-text 不灵活,会覆盖子元素
-
v-pre 不解析,原封不动的展示出来
-
v-cloak(一般用不到,了解) vue在解析后,会把v-cloak的属性都删除掉。
<style> [v-cloak] {
display:none
} </style>这样就不会让页面从 {{message}变为message的值,而是从空白变成message的值
- v-on (语法糖@事件)监听事件
// 手动获取浏览器的event参数 $event @click="clickMe(item,index,$event)"
- v-on修饰符
1.stop
<div id="app" @click='divClick'> // .stop 阻止事件冒泡,调用stopPropagation <button @click.stop='btnClick'>button</button> </div>
2.prevent
<form action="www.baidu.com"> // .prevent 阻止默认事件 <input @click.prevent="submit" type="submit" value="提交" /> </form>
3.
// . 监听键帽{keyCode||keyAlias} 别名 <input type="text" @keyup.13="keyUp"> <input type="text" @keyup.enter="keyUp">
4.once
// 只能点击一次 <button @click.once='onceClick'>一次</button>
注意 可以链式使用
@click.prevent.stop
-
v-bind (语法糖:属性)动态的绑定属性
v-bind:src="imgSrc" 就可以把变量的值放到属性中
mustache不能在属性里使用,只能在元素的content里使用。
:class可以和class共存
数组表达式:class=[class1,class2]
:style 要带单可以:style="{fontSize:a+'px'}"
- v-model 实现表单元素和数据的双向绑定,其实是一个语法糖,本质上是 利用v-on监听input事件,v-bind绑定value值。然后在methods中定义inputChange方法来修改message的值
背后本质
<input type="text" v-model='message'>{{message}} <!-- 等价于 --> <input type="text" :value='value' @input='inputChange'>{{value}}
v-model修饰符
全局组件 (运行时构建的Vue,其中模板编译器不可用)
Vue.component('abc',{ template:`<div>666</div>` })
父子组件通信
父传子: props
<tab-bar-item v-for="(item,index) in tabList" :key="item.name" :activeColor='item.activeColor' :defaultIndex='0' > <img slot='item-icon' :src="item.icon" > <img slot='item-icon-acitve' :src="item.activeIcon" > <div slot='item-text' >{{item.name}}</div> </tab-bar-item> // 子获取 // props数据验证 props:{ //props是组件的 不是插槽的 所以要给子组件传 不要给插槽传 activeColor:{ type:String, default:'blue',
required: true }, defaultIndex:[Number,String], //多个类型
active:Boolean,
arr:{
type:Array, //类型是对象或者数组时 默认值必须是一个函数,不然会报错
default(){
return []
}
}
父监听子
@sonClick='fatherListener' fatherListener(params){ console.log('让我康康监听子组件事件',params) }
子发送父
@click='sendFather' sendFather(){ this.$emit('sonClick',{name:'Micheal'}) }
this.$refs.cpn // 父获取子 推荐使用 这个要在 <cpn ref="cpn" /> 标注才有效this.$emit('rwkk',{title:'让我康康}) //子组件点击发送给父组件 推荐使用<cpn @rwkk="rwkkChange" > //父组件监听子组件发送的事件 rwkkChange(data){ console.log('让我康康data',data) } // 让我康康data , {title:'让我康康'}this.$parent //子获取父,不推荐使用this.$children // 访问子组件的集合 不推荐使用this.$root //访问根组件
- v-slot 插槽,让组件具备扩展性 可以联想到USB插槽
// 子组件 <template id='cpn'> <div> <p>▼▼▼▼▼</p> <slot> <input type="text" placeholder="我是默认插槽的内容"> </slot> <p>▲▲▲▲▲</p> </div> </template> // 父组件 <cpn><div>默认插槽不生效</div></cpn> <cpn></cpn> //什么内容都没有,默认插槽生效
插槽的name 可以使其不被替换掉
<template id='navigation'> <div > <div style='display:flex;align-items: center;'> <slot name="left"><div>left</div></slot> <slot name="center" > <div style="margin: 0 10px;">center</div></slot> <slot name="right"><div>right</div></slot> </div> <slot>没有name就会被替换掉</slot> </div> </template>
<cpn2>
<slot>
替换没有name的插槽
</slot>
</cpn2>
- v-slot具名插槽的传值
子组件
<template id='cpn'> <div> <slot name='slot2' :list ="languages" :message="message" :happy='happy'></slot> </div> </template>
const app = new Vue({ el: '#app', components:{ cpn:{ template:'#cpn', data(){ return { languages:['JavaScript','Go','Java','Python','C++','Swift','C#'], message:'李小龙', happy:'快乐' } } } } });
父组件使用v-slot
<cpn> <template v-slot:slot2="data"> <span>{{data}}</span> </template> </cpn>
可以直接打印出传的所有数据
模块化
- 常见的模块化规范 CommonJS、AMD、CMD,还有ES6的Modules
核心是: 导入和导出
NodeJS中的模块化 是使用了CommonJS规范 的
导出: module.exports = { }
引入:let/const {a,b,c} = require('../../file.js')
ES6模块化
<script type="module"></script> 只有type为module时 模块化才会生效
let a = 'a'
导出 export { a }
导出Class 类 export class Person { }
导入 import {a} from '../file.js'
通配符导入 1.导入全部 2.解决命名冲突
import {a,b,c,d,e,f.....} from '../file.js' // 东西太多了
import { a,navgation } from '../file.js' import { navigation } from 'vue-navitagion'; 命名冲突 //解决方法 import * as abc from '../file.js' //直接使用abc.navigation 就可以了
Webpack核心
- 会自动打包依赖文件,所以只要把main.js 文件打包,它里面的其他文件也会被跟着打包
webpack ./src/main.js ./dist/bundle.js // 打包命令
首先安装 vue-loader 和 vue-templaye-compiler
yarn add vue-loader vue-template-compiler -D
webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { module: { rules: [ // ... 其它规则 { test: /\.vue$/, loader: 'vue-loader' } ] }, plugins: [ // 请确保引入这个插件! new VueLoaderPlugin() ] }
在客户端编译模板,需要加上编译器
// 需要编译器 new Vue({ template: '<div>{{ hi }}</div>' }) // 不需要编译器 new Vue({ render (h) { return h('div', this.hi) } })
你在最终打好的包里实际上是不需要编译器的,所以只用运行时版本即可(运行版体积比完整版小30%)
使用完整版,则需要在打包工具里配置一个别名
module.exports = { // ... resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js' } } }
package.json配置
"scripts": { "build": "webpack", "dev": "webpack-dev-server --open" },
启动yarn dev 报错
Error: Cannot find module 'webpack-cli/bin/config-yargs'
找到原因是因为webpack-cli
的新版本(4.x)对webpack-dev-server(3.x)
版本的不兼容
降低webpack-cli版本为3.3.12(3.x)即可
- webpack 配置分离
安装依赖
yarn add webpack-merge -D
把 webpack.config.js中的内容进行分离
prod 生产环境的config.js
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); const baseConfig = require('./base.config') const { merge } = require('webpack-merge') module.exports = merge(baseConfig, { plugins: [ new UglifyjsWebpackPlugin() ], })
dev 开发环境的config.js
const baseConfig = require('./base.config'); const { resolve } = require('path') const { merge } = require('webpack-merge'); module.exports = merge(baseConfig,{ devServer: { contentBase: resolve(__dirname, 'build'), // 启动gzip压缩 compress: true, //端口号 port: 3000, open: true, //开启HMR功能 hot: true, }, })
base 公共的config.js
const {resolve} = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './base/src/index.js', output: { path: resolve(__dirname, '../dist'),//这样就可以把dist文件夹放出去 filename: 'bundle.js', }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'], }, { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: {}, }, }, { test: /\.vue$/, loader: 'vue-loader', }, ], }, resolve: { alias: { vue$: 'vue/dist/vue.esm.js', // 用 webpack 1 时需用 'vue/dist/vue.common.js' }, }, plugins: [ // 请确保引入这个插件! new VueLoaderPlugin(), new HtmlWebpackPlugin({ // 复制'./index.html' 文件, 并自动引入打包输出的所有资源(JS/CSS) 会自己创建script标签 并引入资源 template: './index.html', //压缩html代码 minify: { //移除空格 collapseWhitespace: true, //移除注释 removeComments: true, }, }), ] };
这时候就可以删除webpack.config.js文件了 (这个文件是默认文件)
删除后 在package.json里配置,即可指定webpack配置文件
"scripts": { "build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --config ./build/dev.config.js" },
前端路由 (hash模式和history模式)
不会加载所有js, 会根据判断来加载
hash模式:
早期的前端路由的实线就是基于localtion.hash来实现的,localtion.hash就是#后面的内容,其原理就是监听#后面的内容的变化来发送ajax请求进行更新,这里使用hashchange来监听URL的变化,使用hash模式有两个特点:
- 改变hash值,浏览器不会重新加载页面
- 当刷新页面时,hash不会传给服务器
history模式:
history是基于HTML5新增的pushState()和replaceState()两个api以及浏览器的popState事件监听历史栈的改变,只要历史栈有信息发生改变的话,就会触发该事件,当时用pushState()和replaceState()对URL进行修改时,浏览器不会刷新,在history模式下,URL没有#这样更美观一些,比如http://www.baidu.com/my/detail,但是注意的是前端的URL必须和实际向后端发送请求的URL一致,而且还需要后台配置的支持,如访问http://www.baidu.com/my/detail时,如果后台没有对/my.detail的路由处理,将返回404错误,pushState()会添加历史记录,而replaceState()不会
栈结构:相当于你往杯子里放草莓,越早放进去的越后拿出来,要拿只能从最后一个开始拿。
使用history.back()和history.forward() 和history.go(1) 就会使用栈结构
pushState(state,title,url)和replaceState(state,title,url)都可以接受三个参数,state指的是需要保存的数据,这个数据在触发popState事件时可以在event.state中获取,title没什么用,一般给null就行,url指设置新的url记录,通过修改url值进行页面的跳转
404错误
1、hash模式下,仅hash符号之前的内容会被包含在请求中,如 http://www.abc.com 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误;
2、history模式下,前端的url必须和实际后端发起请求的url一致,如http://www.abc.com/book/id 。如果后端缺少对/book/id 的路由处理,将返回404错误。
作者:大黑豹
链接:https://www.jianshu.com/p/ae8f9c41a77c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
单页面应用阶段:
其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由。
也就是前端来维护一套路由规则
后端渲染: 后端在服务器把页面渲染好,直接给用户展示。
前端渲染:从后端的API接口里面,把数据请求过来。利用js渲染
2.在某些场合中,用ajax请求,可以让页面无刷新,页面变了但Url没有变化,用户就不能复制到想要的地址,用前端路由做单页面网页就很好的解决了这个问题。但是前端路由使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。
- replace 修改vue的路由跳转模式,默认是pushState
- tag 更换标签 vue3.x 不生效
自定义路径
vue-cli4 vue.config.js文件 (和package.json同级)
const path = require('path');//引入path模块 function resolve(dir){ return path.join(__dirname,dir)//path.join(__dirname)设置绝对路径 } module.exports={ chainWebpack:(config)=>{ config.resolve.alias .set('@',resolve('./src')) //vue已经内置了 .set('components',resolve('./src/components')) .set('views',resolve('src/views')) .set('assets',resolve('src/assets')) //set第一个参数:设置的别名,第二个参数:设置的路径 } }
路由懒加载(只有跳转到页面时,才会加载组件,代码切割)
const Home = () => import('views/Home/Home.vue') //懒加载 const Shop = () => import('views/Shop/Shop.vue') //懒加载 const Profile = () => import('views/Profile/Profile.vue') //懒加载 const Category = () => import('views/Category/Category.vue') //懒加载
路由切换
路由懒加载(用到时才加载),只有当跳转到页面时,才会加载组件,代码切割。
这里要注意区别 $router 和 $route
this.$router.push/replace
itemClick (item, index) {this.$router.replace(item.path) }
动态路由 /:id, router-link 就是 :to
查看路由携带参数和其他属性 this.$route.params / this.$route
路由跳转携带参数
this.$router.push({ path:item.path, query:{ item } })
router-link 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to
属性指定目标地址,默认渲染成带有正确链接的 <a>
标签,可以通过配置 tag
属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名(active-class=‘xxx’)。
也可以直接在路由文件中配置
export default new Router({ linkActiveClass: 'active', routes: [ ] })
带参数
<router-link :to="{path:'/profile',query:{name:'Micheal'}}" tag='div' active-class='home-class'>首页</router-link>
Vuex
- actions 异步操作
- mutations 同步操作
- getters 计算属性
- state 数据存储
- modules 可以让每一个模块拥有自己的state、mutations、actions、getters,使得结构非常清晰,方便管理。
获取store中的所有方法、属性
this.$store
调用store中的同步事件
this.$store.commit('xxx')
调用store中的异步事件
this.$store.dispatch('xxx')
调用getters中的计算方法
this.$store.getters.totalNumber
使用modules
const moduleA = { state: {}, mutations: {}, actions: {}, getters: {}, }; modules: { a: moduleA },
抽离出去 方便管理
store/index.js
import Vue from 'vue'; import Vuex from 'vuex'; import getters from './getters.js'; import mutations from './mutations.js'; import actions from './actions.js'; import modules from './modules.js'; Vue.use(Vuex); export default new Vuex.Store({
//这个不用抽离 state: { params1: 123, params2: 456, }, getters, mutations, actions, modules, });
actions.js
export default { asyncFunc(){ setTimeout(() => { console.log('让我康康异步操作') }, 1000); } }
固定中间内容高度的两种方式
1. calc (100% - top -bottom)
2.position: absolute; top: 44px;bottom: 50px;
$el: 获取vue实例相关DOM元素
加设要获取组件A的offsetTop,就可以使用$el来获取
this.$refs.refA.$el.offsetTop
error修复
1.重复replace点击当前路由
解决方法:在router.js文件中添加
const originalReplace = VueRouter.prototype.replace VueRouter.prototype.replace = function replace(location) { return originalReplace.call(this, location).catch(err => err) }
核心思想:
1.数据驱动 所谓的数据驱动就是当数据发生变化的时候,用户界面发生相应的变化,开发者不需要手动的去修改dom。
https://www.cnblogs.com/caizhenbo/p/6418284.html 数据驱动原理
2.组件化
1. 处理类 : 先给class加v-bind绑定 => :class , 然后再三元表达式 :class = "['mui--control-item',item.id == 0 ? ' mui-active' : ' ' ]"
2. 处理路由高亮
- : MUI库中默认的高亮是 ' mui-acrive', (推荐)
路由配置的第二个参数 用这个去覆盖默认高亮类 router-link-active
- 把router-link-active 的样式改了 (不推荐 因为要设置样式表 还要考虑优先级 很麻烦)
3. 视图切换样式 (包住路由的区域,因为只有路由在切换的时候才需要改造样式,其他都是固定的)
1 .v-enter {
2 opacity: 0;
3 transform: translateX(100%);
4 }
5
6 .v-leave-to {
7 opacity: 0;
8 transform: translateX(-100%);
9 position: absolute;
10 }
11
12 .v-enter-active,
13 .v-leave-active {
14 transition: all 0.5s ease;
15 }
<transition>
<router-view></router-view>
</transition>
4. 阻止页面切换时出现滑动条
1 .app-container {
2 overflow-x: hidden;
3 }
5.把router-link标签 指定渲染成li标签 => 加tag属性 tag= “ li ”
6.router-link跳转到带有id的地址上:
如果是普通的跳转 直接to 就行,但如果地址带有id,name就要改造to 因为item.id是一个表达式
1 <router-link v-for="item in list" :key="item.id" :to="'/home/photoinfo/' + item.id" tag="li">
7.编程式导航 意思就是用js来跳转页面,项目中用到最多的就是当点击某个按钮时判断当前登录没登录,没有登录点击的时候跳转到登录页面。距离
1 <template> 2 3 <button @click="getNews()">通过js跳转</button> 4 5 </template> 6 7 <script> 8 9 export default{ 10 11 data(){ 12 13 return{ 14 15 } 16 17 }, 18 19 methods:{ 20 21 getNews(){ 22 23 this.$router.push({path:'./login'})//这种是通过path 24 25 this.$router.push({path:'./login/1'})//这种是动态路由 26 27 this.$router.push({name:'login',params:{id:1}})//这种是命名路由,路由配置时候的name,还可以传参数 28 29 } 30 31 } 32 33 } 34 35 </script>
8.导航前进后退
this.$router.go(1);
this.$router.go(-1);
9. router-view详解 https://www.cnblogs.com/yuerdong/p/9877912.html
10.$route 和 $ router 的区别 https://www.cnblogs.com/czy960731/p/9288830.html
1. this.$router:
表示全局路由器对象,项目中通过router路由参数注入路由之后,在任何一个页面都可以通过此方法获取到路由器对象,并调用其push(), go()等方法;
2. this.$route:
表示当前正在用于跳转的路由器对象,可以调用其name、path、query、params等方法;
注:使用的时候注意拼写,两个很像,就差一个字母,经常会因为写错而导致调用的方法无效,而且浏览器的控制台中还不会报错
有时的需求是页面不直接跳转,有确认弹框或者其他事件,此时就需要在js中设置跳转,常用的一种方法是 .$router.push ,用法如下:
this.$router.push({path: '/...'});
11. v-if 和 v-show
v-if appendChild / removeChild 如果在运行时条件不大可能改变 v-if 较好。
v-show style.display = ‘block’ / ‘none ’ 如果是频繁切换,v-show较好。
v-for 优先级最大
12. v-on和v-bind
v-on:原生js的事件名 = ‘逻辑’
v-on:原生js事件名 = ‘方法名’ 该方法必须在methods內
常用
1 v-on:input 对应HTML5中新增的oninput事件 类似于onchange
v-bind:标签中的原生属性
v-bind:自定义属性
单向数据绑定 data ======> view
v-model 只能应用在表单控件 value UI控件(单选框 多选框)
13 .父子通信
ref和$ref的区别 https://www.cnblogs.com/xumqfaith/p/7743387.html
子组件传值父组件: $ref实现子组件向父组件通信 ===>如果ref用在子组件上,是作为子组件的一个索引,通过$ref可以获取到子组件里面的属性和方法。如果ref在dom元素上面使用,可以通过ref获取到该dom的属性集合。
prop和$ref的区别是: 尽管有 prop 和事件,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref
为子组件指定一个引用 ID
prop 官网解释 https://cn.vuejs.org/v2/guide/components-props.html
prop是用于父组件向子组件传递数据(单向下行绑定)。 直接写 xx = abc 静态赋值 ,v-bind:xx='list.title' 动态赋值 https://www.cnblogs.com/xiaohuochai/p/7388866.html
1 //1父组件在子组件的标签内自定义要传值的属性给子组件 2 <test1 happy='123'></test1> 3 //子组件声明后,在template中直接用插值表达式使用 4 <template> 5 <div> 6 <input type ='button' @click='info' value='点我'> 7 <span>{{happy+'hello'}} </span> 8 </div> 9 </template> 10 <script> 11 export default{ 12 data(){ 13 return { 14 foo:1 15 } 16 }, 17 props:['happy'], 18 methods: { 19 info:function(){ 20 console.log(this.happy) 21 } 22 } 23 } 24 </script>
$ref着重于子组件的索引,带领父组件查找到子组件的属性和方法,并不适合用来做数据之间的通信。
3.$emit和$ref以及$on的区别
用ref为子组件赋一个标记引用
1 <p ref="username">传值给父组件</p>
在已经定义了这个 ref
的组件里,可以使用
console.log(this.$refs.username)
来访问这个 <p>
实例,以便不时之需
http://www.cnblogs.com/developer-sue/p/9434425.html
https://blog.csdn.net/wngzhem/article/details/53409561
子组件可以通过props接收到来自父组件的数据,并且是单向绑定的。也就是说,只能收数据。
vuejs官网解释 https://cn.vuejs.org/v2/guide/instance.html
父组件使用$refs获得子组件实例
//父组件
1 <template> 2 <div id="app"> 3 <!-- <test></test> --> 4 <test1 happy='123' ref="btn"></test1> 5 <img src="./assets/logo.png"> 6 <input type='button' value='聚焦子组件' @click='focus'></input> 7 </div> 8 </template> 9 10 <script> 11 import test from './components/test' 12 import test1 from './components/test1' 13 export default { 14 name: 'App', 15 components:{ 16 test, 17 test1 18 }, 19 data(){ 20 return { 21 foo:2 22 } 23 }, 24 methods: { 25 baz: function () { /* ... */ }, 26 focus: function () { 27 console.log(this.$refs.btn.dangerous) 28 //console.log(this.$refs.btn) 29 } 30 } 31 } 32 </script>
子组件
1 <template> 2 <div> 3 <input type ='button' @click='info' value='点我'> 4 <span>{{happy+'hello'}} </span> 5 <p ref="username">传值给父组件</p> 6 </div> 7 </template> 8 <script> 9 export default{ 10 data(){ 11 return { 12 foo:1,
//这里的值将被父组件得到 13 dangerous: '危险的' 14 } 15 }, 16 props:['happy'], 17 methods: { 18 info:function(){ 19 //console.log(this.happy) 20 console.log(this.$refs.username) 21 } 22 } 23 } 24 </script>
切记: 想用ref 获取那个子组件的实例 就在那个子组件的标签上添加ref属性,要传那个属性给儿子就在那个儿子身上绑定自定义属性(props接收)
$emit 子组件给父组件传值 $on监听当前实例$emit事件
1 vm.$on('test', function (msg) {
2 console.log(msg)
3 })
4 vm.$emit('test', 'hi')
5 // => "hi"
父组件模板:
1 <template> 2 <div id="app"> 3 <!-- <test></test> --> 4 <h1>{{title}}</h1> 5 <test1 happy='123' ref="btn" @getMessage="showMsg"></test1> 6 <img src="./assets/logo.png"> 7 <input type='button' value='聚焦子组件' @click='focus'></input> 8 </div> 9 </template> 10 11 <script> 12 import test from './components/test' 13 import test1 from './components/test1' 14 export default { 15 name: 'App', 16 components:{ 17 test, 18 test1 19 }, 20 data(){ 21 return { 22 foo:2, 23 title:'标题为空' 24 } 25 }, 26 methods: { 27 baz: function () { /* ... */ }, 28 focus: function () { 29 console.log(this.$refs.btn.dangerous) 30 //console.log(this.$refs.btn) 31 }, 32 showMsg:function(title){ 33 this.title =title 34 } 35 } 36 } 37 </script>
子组件模板:
1 <template> 2 <div> 3 <input type ='button' @click='info' value='点我'> 4 <input type ='button' @click='click' value='改标题'> 5 <span>{{happy+'hello'}} </span> 6 <p ref="username">传值给父组件</p> 7 </div> 8 </template> 9 <script> 10 export default{ 11 data(){ 12 return { 13 foo:1, 14 dangerous: '危险的' 15 } 16 }, 17 props:['happy'], 18 methods: { 19 info:function(){ 20 //console.log(this.happy) 21 console.log(this.$refs.username) 22 }, 23 click:function(){ 24 this.$emit('getMessage', '标题被我改回来了') 25 } 26 }, 27 mounted(){ 28 this.$emit('getMessage', '标题被我占了') 29 } 30 31 } 32 </script>
三点对比 :
- prop 着重于数据的传递,它并不能调用子组件里的属性和方法。像创建文章组件时,自定义标题和内容这样的使用场景,最适合使用prop。
- $ref 着重于索引,主要用来调用子组件里的属性和方法,其实并不擅长数据传递。而且ref用在dom元素的时候,能使到选择器的作用,这个功能比作为索引更常有用到。
- 前门两种方法主要都是父组件向子组件通信,而通过$emit 实现子组件向父组件通信
15. watch 用来监测Vue实例上的数据变动。
https://www.cnblogs.com/jin-zhe/p/9319648.html
16.v-bind:key 增删或切换顺序时提高性能
17.template嵌套template
template不会渲染成元素,用div的话会被渲染成元素。把if,show,for等语句抽取出来放在template上面,把绑定的事件放在temlpate里面的元素上,可以使html结构更加清晰,还可以改善一个标签过长的情况
1.vue-cli脚手架里面的 vue ui
1 首先安装 最新的vue-cli cnpm install -g @vue/cli 2 Terminal 输入vue ui命令
18. watch :{ } 监听 ,
记住了 只有data 和生命周期钩子函数 才是 (){ } ES6匿名函数写法
methods / computed / watch 都是 : { }
watch、computed、methods的对比
watch/computed 共同点: 只要数据一变化就可以监视到
1、computed不适合写大量的业务逻辑,只适合做一些简单的数据操作,然后并给它return 出去就完事了。 添油加醋的修改 不要改本身)
2、computed属性的结果会被缓存。除非依赖的响应式属性变化了才会重新计算,主要当做属性来使用。
3、methods 方法表示一个具体的操作,主要书写业务逻辑。
4、watch 是一个对象,它的键 是我们要观察的表达式,值是对应的回调函数。当前面观察的值发生变化的时候,会触发我们的回调函数。在回调函数的形参中,有两个参数:一个是newVal(最新的值),一个是oldVal(旧值)。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是 computed 和 methods 的结合体。
5、computed和watch 都可以监视到,数据的变化。methods里只能做一些业务逻辑。watch里面也能做业务逻辑
19. vue样式的切换
官方文档 :https://cn.vuejs.org/v2/guide/class-and-style.html
实例1:
两个值相等的时候 true active生效
点击的时候 当前的curNav等于当前的content
实例2: 地图切换
效果图:
代码:
1 <div class="select-gpu"> 2 <div :class="{'gpu-type':true,active:chart_type=='gtx 1050ti'}" @click="chartHandle('gtx 1050ti')">gtx 1050ti</div> 3 <div :class="{'gpu-type':true,active:chart_type=='gtx 1080ti'}" @click="chartHandle('gtx 1080ti')">gtx 1080ti</div> 4 </div>
1 <style lang="less"> 2 .select-gpu { 3 display: inline-block; 4 margin-left: 70%; 5 .gpu-type { 6 display: inline-block; 7 background-color: #eee; 8 min-width: 40px; 9 padding: 0 10px; 10 text-align: center; 11 font-size: 14px; 12 border-radius: 10px; 13 margin-left: 10px; 14 cursor: pointer; 15 line-height: 22px; 16 } 17 .active { 18 color: #fff; 19 background-color: #7baaf3; 20 } 21 } 22 </style>
export default { data(){ return{ chart_type: "gtx 1050ti" } } methods:{ chartHandle(value) { if (value === "user") { this.search_type = value; } else { this.search_type = "gpu"; this.gpu = value; } this.chart_type = value; this.getGpuData(); }, }
数据切换: ======> 通过参数名的改变而改变
20.vue 组件切换
1 <div :class="{'resource-nav-item':true,active:curNavContent === item.content}" @click="tabChange(item)">
1 <component v-bind:is="curNavContent"></component>
1 navList:[{name:'艺术滤镜',content:'artFilters',description:',又称为风格迁移,可以使一张图片保持本身内容大致不变的情况下呈现出另外一张图片的风格。以往,基于PhotoShop,我们费时费力才能实现风格的转换。现在,基于最新的人工智能技术,我们可以一次性实现多种多样的、风格逼真的艺术风格变换。'}
2 ,{name:'目标检测',content:'Object',description:',是从图像中辨识感兴趣目标的类型与位置。借助于人工智能深度学习技术,我们不仅能识别人、车、动植物、常用家具等目标,还能准确地标识目标的轮廓。'}],
3 curNavContent: 'artFilters',//当前组件名
1 components: { artFilters: () => import("@/views/lab/art_filters"),
2 Object: () => import("@/views/lab/object_detection") }
1 tabChange(row) {
2 this.curRow = row;
3 this.curNavContent = row.content;
4 }
21. 插槽 slot-scope(已废弃) v-slot (2.6新增)
插槽的含义: 简单的说,插槽就是可以让开发者自定义地往子组件中放置代码片段而开发出来的东西。就好像专门在某几个地方弄了几个槽(子组件中),我们开发时,就可以在对应的槽中放置对应的代码了。
官方安装
1 # 全局安装 vue-cli 2 $ cnpm install --global vue-cli 3 # 创建一个基于 webpack 模板的新项目 4 $ vue init webpack my-project 5 # 这里需要进行一些配置,默认回车即可 6 This will install Vue 2.x version of the template. 7 8 For Vue 1.x use: vue init webpack#1.0 my-project 9 10 ? Project name my-project 11 ? Project description A Vue.js project 12 ? Author runoob <test@runoob.com> 13 ? Vue build standalone 14 ? Use ESLint to lint your code? Yes 15 ? Pick an ESLint preset Standard 16 ? Setup unit tests with Karma + Mocha? Yes 17 ? Setup e2e tests with Nightwatch? Yes 18 19 vue-cli · Generated "my-project". 20 21 To get started: 22 23 cd my-project 24 npm install 25 npm run dev 26 27 Documentation can be found at https://vuejs-templates.github.io/webpack