MVVM:就是Model-View-ViewModel 的缩写
Model:表示数据模型,在model中定义数据修改和业务逻辑
View:表示视图层,负责将数据转化为UI展示出来
ViewModel:监听模型数据的改变和控制视图行为,以及用户交互,也就是同步model和view的对象,ViewModel通过双向数据绑定将view层和model层连接起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
beforeCreate:(创建前),数据观测和初始化事件还未开始
created:(创建后),完成数据观测,属性和方法的运算,初始化事件,此时$el属性还未显示出来
beforeMount:(载入前),在 e l 被 新 创 建 的 v m . el被新创建的vm. el被新创建的vm.el替换,并挂载到实例上去之后调用,实例以及完成以下配置:用上面编译好的html内容替换$el属性指向DOM对象,完成模型中的html渲染到html页面中,此过程进行ajax 交互。
beforeUpdate:(更新前),在数据更新之前调用,发生在DOM重新渲染和打补丁之前,可以在该钩子中进一步更新状态,不会触发附加的重新渲染过程。
updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
1.什么是vue生命周期?
答:vue从创建到销毁就是生命周期,从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
2.vue生命周期的作用是什么?
答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
3.vue生命周期总共有几个阶段?
答:它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。
4.第一次页面加载会触发哪几个钩子?
答:会触发 下面这几个beforeCreate, created, beforeMount, mounted 。
5.DOM 渲染在 哪个周期中就已经完成?
答:DOM 渲染在 mounted 中就已经完成了。
vue实现双向数据绑定主要采用数据劫持结合发布者-订阅者模式,通过Object.defineProperty()来劫持个个属性的setter,getter,在数据发生变化时通知订阅者,触发相应的回调函数,
简单的js实现双向绑定
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {
}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
在父组件中引入子组件,在自定义属性名
在子组件中使用 props 接收
子组件:{
{message}}
*** props 两种写法
props: {
name: {
type: String,
default:'Kobe Bryant'
}
}
props: ["message"]
子组件通过点击事件触发,$emit方法传送参数
在父组件中用$on接收子组件传递过来的参数
使用eventBus.js文件,相当于一个中转站,用于接收和发送事件,项目小的时候可以使用
步骤如下:
创建Bus.js文件
import Vue from "vue";
export default new Vue();
第一个组件 first.vue
import Bus from '../bus.js';
export default {
name: 'first',
data () {
return {
value: '我来自first.vue组件!'
}
},
methods:{
add(){// 定义add方法,并将msg通过txt传给second组件
Bus.$emit('txt',this.value);
}
}
}
第二个组件second.vue
import Bus from '../bus.js';
export default {
name: 'second',
data () {
return {
}
},
mounted:function(){
Bus.$on('txt',function(val){//监听first组件的txt事件
console.log(val);
});
}
}
**hash模式:**在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
**history模式:**history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next:
to:route即将进入的目标路由对象,
from:route当前导航正要离开的路由
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,…… export 。
场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
const store = new Vuex.Store({ //store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
})
在main.js中创建全局指令
// 自定义全局指令
Vue.directive("dir1", {
inserted(el) {
el.style.width = '200px';
el.style.height = '200px';
el.style.background = 'red';
}
})
{
{txt1|text_arr}}
在main.js中注册全局过滤器
// 全局过滤器
Vue.filter('str_change', (data) => {
console.log(data)
})
1、如果通过v-for 遍历想加不同的ref时记得加 :
号,即 :ref =某变量
;
这点和其他属性一样,如果是固定值就不需要加 :
号,如果是变量记得加 :
号。(加冒号的,说明后面的是一个变量或者表达式;没加冒号的后面就是对应的字符串常量(String))
2、通过 :ref =某变量
添加ref(即加了:
号) ,如果想获取该ref时需要加 [0]
,如this.$refs[refsArrayItem] [0]
;如果不是:ref =某变量
的方式而是 ref =某字符串
时则不需要加,如this.$refs[refsArrayItem]。
1、ref 需要在dom渲染完成后才会有,在使用的时候确保dom已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。
2、如果ref 是循环出来的,有多个重名,那么ref的值会是一个数组 ,此时要拿到单个的ref 只需要循环就可以了。
html
<Button @click="choiceImg" icon="ios-cloud-upload-outline" type="primary">点击上传</Button>
<input ref="filElem" type="file" >
script
choiceImg(){
this.$refs.filElem.dispatchEvent(new MouseEvent('click'))
},
1,在components文件内创建一个button文件,文件内创建一个index.vue文件,在index.vue文件内写的是原型(包含组件的名字,应用的最底层的HTML标签,分别根据什么条件显示什么功能),同时该文件导出的数据为一个对象。
2.在button文件下建立一个index.js文件,文件内对新构建组件的名字进行注册。
import Button from "./index.vue";
Button.install = (Vue)=>{
Vue.component(Button.name,Button)
}
export default Button;
3.与button文件同级建立一个index.js文件,对组件进行注册,同时也注册进install中,在导出时,不仅要引出全局的,而且单个的也要引出,便于局部或全局引用。
import Button from "./button"
const components = [
Button
]
//vue。use使用时,必须要有install方法。参数就是vue。
const install = (Vue)=>{
for(var key in components){
Vue.component(components[key].name,components[key])
}
}
export default {
install,
Button
}
4.在main.js中进行引用
import Vue from 'vue'
import App from './App.vue'
import AlleyUI from "./components"
Vue.config.productionTip = false
Vue.use(AlleyUI);
new Vue({
render: h => h(App),
}).$mount('#app')
5.到这里,组件便是封装完成了,在App.vue中可以进行使用了。
按钮
按钮
按钮
按钮
匿名插槽
子组件中写入slot插槽
今天天气怎么样
父组件中往注册的子组件中填充内容
我是父组件
多云
总结:父组件填充内容到子组件标签中,子组件写上slot插槽,则会显示填充的内容
匿名插槽解析
具名插槽其实就是给插槽娶个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中
子组件中写入多个具名插槽
父组件中使用“v-slot:插槽名称”
我是父组件
头部
尾部
this.$nexTick()解析
将回调函数延迟到下次 DOM 更新循环之后执行,修改数据之后立即使用它
{
{ message }}
这个是没用的nextTick获得的消息:{
{ messageOne }}
这个是我用的nextTick后获得的消息:{
{ messageTwo }}
一个组件只能定义一个v-model双向数据绑定,如果其他的也要用到双向数据绑定,则使用.sycn修饰符
场景:父组件传值 子组件弹出窗
父组件中使用自组件
<Alerts :alerts.sync="alert_type" v-on:update:alerts="alert_type=$event"></Alerts>
子组件中接受参数
props: {
alerts: {
type: Boolean,
default: ''
},
},
子组件关闭窗口
_this.$emit('update:alerts', false)
// 父组件中
v-on:update:alerts="alert_type=$event"
其他按钮触发弹窗
修改点击按钮的绑定值
@click="alerts_=true"
// 利用计算属性传值
computed:{
alerts_:{
get(){
return this.alerts
},
set(val){
this.$emit('update:alerts',val)
}
}
}
1. 不带参数
//name,path都行, 建议用name
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
2.带参数
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id
1. 不带参数
this.$router.push('/home')
this.$router.push({
name:'home'})
this.$router.push({
path:'/home'})
2. query传参
this.$router.push({
name:'home',query: {
id:'1'}})
this.$router.push({
path:'/home',query: {
id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
this.$router.push({
name:'home',params: {
id:'1'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
注意*** 由于动态路由也是传递params的,所以在this.$router.push() 的方法中, path不能和params一起使用,否则params将无效。需要用name来指定页面。
及通过路由配置的name属性访问
区别在于,这个方法不会向history里面添加新的记录,点击返回,会返回到上上个记录,上一个记录是不存在的。
相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)
。n可为正数可为负数。正数返回上一个页面
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
ps : 区别
this. r o u t e r . p u s h 跳 转 到 指 定 u r l 路 径 , 并 想 h i s t o r y 栈 中 添 加 一 个 记 录 , 点 击 后 退 会 返 回 到 上 一 个 页 面 t h i s . router.push 跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面 this. router.push跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面this.router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
直接给路径不会生效,需要require引入,图片才会在页面中生效
// js中
progoImgs: [
{
img: require("@/pages/user/images/[email protected]"),
texts:"书籍容量"
}
]
本文属于个人积累,如果不对,还请指出!