谈谈Vue中的MVVM模式
MVVM全称是Model-View-ViewModel
Vue是以数据为驱动的,Vue自身将DOM和数据进行绑定,一旦创建绑定,DOM和数据将保持同步。 ViewModel是Vue的核心,它是Vue的一个实例。Vue实例时作用域某个HTML元素上的这个HTML元素可以是body,也可以是某个id所指代的元素。
DOMListeners和DataBindings是实现双向绑定的关键。DataBindings监听Model层的数据,当数据发生变化,View层的DOM元素随之变化。
v-show和v-if指令的共同点和不同点
达到让元素显示和隐藏的效果,if是惰性的,
if 有更高的切换消耗而 show 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 show更好,如果在运行时条件不大可能改变则 if 较好。
keep-alive的作用
缓存组件状态或避免重新渲染
Vue中引入组件的步骤
采用ES6的import … from …语法引入组件
Vue.component对组件进行注册,最后使用组件。
指令v-el的作用
作为 Vue 实例的挂载目标
Vue中常用的生命周期钩子函数
beforecreate : 举个栗子:可以在这加个loading事件
created :在这结束loading,还做一些初始化,实现函数自执行
mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestroy: 你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容
Vuex的原理和使用方法
是什么?
数据单向流动,组件嵌套过多的时候, 多组件共享同一个State会在数据传递时出现很多问题.Vuex就是为了解决这些问题而产生的。
也就是所有组件的数据中心
一个实例化的Vuex.Store由state, mutations和actions三个属性组成,
• state中保存着共有数据
• 改变state中的数据可以通过mutations方法,同步处理
• 如果要写异步的方法,需要写在actions中
active-class是 vue-router模块的router-link组件
嵌套路由怎么定义
需要在 VueRouter 的参数中使用 children 配置,这样就可以很好的实现路由嵌套
定义vue-router的动态路由?怎么获取传过来的动态参数?
path属性加上/:id。
使用router对象的params.id
至少4种vue当中的指令和它的用法
v-if:判断是否隐藏;v-for:数据循环出来;v-bind:class:绑定一个属性;v-model:实现双向绑定
vue-router是什么?它有哪些组件?
vue用来写路由一个插件。router-link、router-view
导航钩子有哪些?它们有哪些参数?
全局钩子和组件内独享的钩子
参数:
有to(去的那个路由)、from(离开的路由)、next(一定要用这个函数才能去到下一个路由,如果不用就拦截)
vue生命周期的理解
创建前/后: 在beforeCreated阶段,可以加个loading 创建后结束这个loading
创建后,vue实例的数据对象data有了
在mounted阶段,vue实例挂载完成,data.message成功渲染
更新前/后、销毁前后
页面加载会触发哪几个钩子
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
DOM 渲染在 mounted 中就已经完成了
vuex的State特性是什么
一、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于与一般Vue对象里面的data
二、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
三、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
axios的特点有哪些
一、Axios 是一个基于 promise 的 HTTP 库,支持promise所有的API
二、它可以拦截请求和响应
三、它可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据
四、安全性更高,客户端支持防御 XSRF
仿豆瓣–案例
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import vueScrollBehavior from 'vue-scroll-behavior'
import 'normalize.css'
Vue.use(vueScrollBehavior, { router: router })
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
import request from 'superagent'
import jsonp from 'superagent-jsonp'
const state = {
hotMovies: [],
newMovies: [],
topMovies: [],
movieTags: [
{
title: '同时入选IMDB250和豆瓣电影250的电影',
href: 'https://m.douban.com/doulist/968362/',
color: '#FFAC2D'
},
{
title: '带你进入不正常的世界',
href: 'https://m.douban.com/doulist/16002',
color: '#FF4055'
},
{
title: '用电【影】来祭奠逝去的岁月',
href: 'https://m.douban.com/doulist/190343',
color: '#4F9DED'
},
{
title: '女孩们的故事【电影】',
href: 'https://m.douban.com/doulist/1125971',
color: '#FFC46C'
},
{
line: true
},
{
title: '科幻是未来的钥匙——科幻启示录【科幻题材】',
href: 'https://m.douban.com/doulist/4253902',
color: '#2384E8'
},
{
title: '美国生活面面观',
href: 'https://m.douban.com/doulist/121326',
color: '#3BA94D'
},
{
title: '2015终极期待',
href: 'https://m.douban.com/doulist/37479562',
color: '#42BD56'
},
{
title: '经典韩国电影——收集100部',
href: 'https://m.douban.com/doulist/458087',
color: '#CC3344'
}
]
}
const mutations = {
getMovie (state, payload) {
switch (payload.tag) {
case 'hotMovies':
state.hotMovies = payload.res
break
case 'newMovies':
state.newMovies = payload.res
break
case 'topMovies':
state.topMovies = payload.res
break
default:
state.hotMovies = payload.res
}
}
}
const actions = {
/**
* Getting movies
* q: in_theaters, coming_soon, top250
* count: 8
*/
getMovie ({ commit }) {
request
.get('https://api.douban.com/v2/movie/in_theaters?count=8')
.use(jsonp)
.end((err, res) => {
if (!err) {
commit({
type: 'getMovie',
tag: 'hotMovies',
res: res.body.subjects
})
}
})
request
.get('https://api.douban.com/v2/movie/coming_soon?count=8')
.use(jsonp)
.end((err, res) => {
if (!err) {
commit({
type: 'getMovie',
tag: 'newMovies',
res: res.body.subjects
})
}
})
request
.get('https://api.douban.com/v2/movie/top250?count=8')
.use(jsonp)
.end((err, res) => {
if (!err) {
commit({
type: 'getMovie',
tag: 'topMovies',
res: res.body.subjects
})
}
})
}
}
export default {
state,
mutations,
actions
}
import Vue from 'vue'
import Vuex from 'vuex'
import movie from './modules/movie'
import activities from './modules/activities'
import book from './modules/book'
import subject from './modules/subject'
import group from './modules/group'
import search from './modules/search'
import user from './modules/user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
movie,
activities,
book,
subject,
group,
search,
user
}
})
<template>
<div class="movie-view has-header">
<scroller title="影院热映" type="hasCover" :items="hotMovies"></scroller>
<scroller title="免费在线观影" type="hasCover" :items="topMovies"></scroller>
<scroller title="新片速递" type="hasCover" :items="newMovies"></scroller>
<scroller title="发现好电影" type="onlyString" :items="movieTags"></scroller>
<types></types>
<download-app></download-app>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Scroller from '../components/Scroller'
import Types from '../components/Types'
import DownloadApp from '../components/DownloadApp'
export default {
name: 'movie-view',
components: { Scroller, Types, DownloadApp },
data () {
return {}
},
computed: {
// Getting Vuex State from store/modules/movie
...mapState({
hotMovies: state => state.movie.hotMovies,
topMovies: state => state.movie.topMovies,
newMovies: state => state.movie.newMovies,
movieTags: state => state.movie.movieTags
})
},
methods: {
// Dispatching getMovie
getMovie: function () {
this.$store.dispatch('getMovie')
}
},
created () {
// Getting movies data on created
this.getMovie()
}
}
</script>
<style scoped>
</style>
vuejs重写csdn实例
<template>
<div>
<!-- 全局header -->
<nv-head :page-type="getTitleStr(searchKey.tab)"
ref="head"
:fix-head="true"
:need-add="true">
</nv-head>
<section id="page">
<!-- 首页列表 -->
<ul class="posts-list">
<li v-for="item in topics" :key="item.id">
<router-link :to="{name:'topic',params:{id:item.id}}">
<h3 v-text="item.title"
:class="getTabInfo(item.tab, item.good, item.top, true)"
:title="getTabInfo(item.tab, item.good, item.top, false)">
</h3>
<div class="content">
<img class="avatar" :src="item.author.avatar_url" />
<div class="info">
<p>
<span class="name">
{{item.author.loginname}}
</span>
<span class="status" v-if="item.reply_count > 0">
<b>{{item.reply_count}}</b>
/{{item.visit_count}}
</span>
</p>
<p>
<time>{{item.create_at | getLastTimeStr(true)}}</time>
<time>{{item.last_reply_at | getLastTimeStr(true)}}</time>
</p>
</div>
</div>
</router-link>
</li>
</ul>
</section>
<nv-top></nv-top>
</div>
</template>
<script>
// webpack-zepto是模块化后的zepto,可以直接通过import导入使用
import $ from 'webpack-zepto';
import utils from '../libs/utils.js';
import nvHead from '../components/header.vue';
import nvTop from '../components/backtotop.vue';
export default {
filters: {
getLastTimeStr(time, isFromNow) {
return utils.getLastTimeStr(time, isFromNow);
}
},
data() {
return {
scroll: true,
topics: [],
index: {},
searchKey: {
page: 1,
limit: 20,
tab: 'all',
mdrender: true
},
messageCount:45,
searchDataStr: ''
};
},
mounted() {
if (this.$route.query && this.$route.query.tab) {
this.searchKey.tab = this.$route.query.tab;
console.log(this.searchKey.tab)
console.log(this.$route.query.tab)
}
console.log(this.store+'----store')
// 如果从详情返回并且之前存有对应的查询条件和参数
// 则直接渲染之前的数据
if (window.window.sessionStorage.searchKey && window.window.sessionStorage.tab === this.searchKey.tab) {
alert('go')
this.topics = JSON.parse(window.window.sessionStorage.topics);
this.searchKey = JSON.parse(window.window.sessionStorage.searchKey);
$(window).scrollTop(window.window.sessionStorage.scrollTop)
this.$nextTick(() => $(window).scrollTop(window.window.sessionStorage.scrollTop));
} else {
// alert('get')
this.getTopics();
}
// 滚动加载
$(window).on('scroll', utils.throttle(this.getScrollData, 300, 1000));
},
beforeRouteLeave(to, from, next) {
// 如果跳转到详情页面,则记录关键数据
// 方便从详情页面返回到该页面的时候继续加载之前位置的数据
if (to.name === 'topic') {
// 当前滚动条位置
window.window.sessionStorage.scrollTop = $(window).scrollTop();
// 当前页面主题数据
window.window.sessionStorage.topics = JSON.stringify(this.topics);
// 查询参数
window.window.sessionStorage.searchKey = JSON.stringify(this.searchKey);
// 当前tab
window.window.sessionStorage.tab = from.query.tab || 'all';
}
$(window).off('scroll');
next();
},
beforeRouteEnter(to, from, next) {
if (from.name !== 'topic') {
// 页面切换移除之前记录的数据集
if (window.window.sessionStorage.tab) {
window.window.sessionStorage.removeItem('topics');
window.window.sessionStorage.removeItem('searchKey');
window.window.sessionStorage.removeItem('tab');
}
}
next();
},
methods: {
// 获取title文字
getTitleStr(tab) {
let str = '';
switch (tab) {
case 'share':
str = '分享';
break;
case 'ask':
str = '问答';
break;
case 'job':
str = '招聘';
break;
case 'good':
str = '精华';
break;
default:
str = '全部';
break;
}
return str;
},
// 获取不同tab的样式或者标题
getTabInfo(tab, good, top, isClass) {
return utils.getTabInfo(tab, good, top, isClass);
},
// 获取主题数据
getTopics() {
let params = $.param(this.searchKey);
$.get('https://cnodejs.org/api/v1/topics?' + params, (d) => {
this.scroll = true;
if (d && d.data) {
d.data.forEach(this.mergeTopics);
}
});
},
mergeTopics(topic) {
if (typeof this.index[topic.id] === 'number') {
const topicsIndex = this.index[topic.id];
this.topics[topicsIndex] = topic;
} else {
this.index[topic.id] = this.topics.length;
this.topics.push(topic);
}
},
// 滚动加载数据
getScrollData() {
if (this.scroll) {
// alert('scroll')
let totalheight = parseInt($(window).height(), 20) + parseInt($(window).scrollTop(), 20);
if ($(document).height() <= totalheight + 200) {
this.scroll = false;
this.searchKey.page += 1;
this.getTopics();
}
}
}
},
watch: {
// 切换页面
'$route' (to, from) {
// 如果是当前页面切换分类的情况
if (to.query && to.query.tab) {
this.searchKey.tab = to.query.tab;
console.log(to.query.tab+'8888888888888888888888')
this.topics = [];
this.index = {};
}
this.searchKey.page = 1;
this.getTopics();
// 隐藏导航栏
this.$refs.head.show = false;
}
},
components: {
nvHead,
nvTop
}
};
</script>