Vue笔记(有点乱)
Vue学习笔记(2019.7.31)
vue
基本指令用法
v-cloak v-text v-html
- v-cloak解决插值表达式闪烁问题
[v-cloak] {
display: none;
}
- v-text会覆盖元素中原本的内容,但是插值表达式只会替换自己的这个占位符,不会把整个元素的内容清空。
<div v-text="msg2"></div>
- v-html,带有html标签的字符串
<div v-html="msg2">1212112</div>
v-bind v-on
- v-bind指令可以被简写为 :要绑定的属性,v-bind 中,可以写合法的JS表达式。
<input type="button" value="按钮" :title="mytitle + '123'">
- v-on
跑马灯
lang() {
if (this.intervalId != null) return;
this.intervalId = setInterval(() => {
var start = this.msg.substring(0, 1)
var end = this.msg.substring(1)
this.msg = end + start
}, 400)
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
},
stop() { // 停止定时器
clearInterval(this.intervalId)
// 每当清除了定时器之后,需要重新把 intervalId 置为 null
this.intervalId = null;
}
v-on
- .stop 阻止冒泡
- .prevent 阻止默认事件
- .capture 添加事件侦听器时使用事件捕获模式
- .self 只当事件在该元素本身(比如不是子元素)触发时触发回调 (只会阻止自己身上冒泡行为的触发,并不会真正阻止 冒泡的行为)
- .once 事件只触发一次
v-model
v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定
使用 v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定
v-model 只能运用在 表单元素中
<div id="app">
<h4>{{ msg }}</h4>
<!-- <input type="text" v-bind:value="msg" > -->
<!-- input(radio, text, address, email....) select checkbox textarea -->
</div>
class
- 数组
:class="['red', 'thin']"
- 数组中使用三元表达式
:class="['red', 'thin', isactive?'active':'']"
- 数组中嵌套对象
:class="['red', 'thin', {'active': isactive}]"
- 直接使用对象
:class="{'red':true,'thin':true,'active':isactive}"
内联样式:
直接在元素上通过 :style
的形式,书写样式对象
:style="{color: 'red', 'font-size': '40px'}"
将样式对象,定义到 data
中,并直接引用到 :style
中
v-for key
- 数组
v-for="(item, i) in list
- 迭代对象中的属性
v-for="(val, key, i) in userInfo
- 数字
v-for="i in 10"
当在组件中使用** v-for 时,key 现在是必须的。
v-if v-show
v-if 的特点:每次都会重新删除或创建元素
v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式
如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show,如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if
filter,filters
- 私有过滤器
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
定义方式:
filters: {
dataFormat(input, pattern = ""){
var dt = new Date(input);
// 获取年月日
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');
...
}
}
使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')来填充字符串;
- 全局过滤器
Vue.filter(
'dataFormat',
function (input, pattern = '') {
...
}
注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!
键盘修饰符
定义:
Vue.config.keyCodes.f2 = 113;
使用自定义的按键修饰符:
<input type="text" v-model="name" @keyup.f2="add">
directive
- 全局自定义指令:
Vue.directive(
'focus', {
inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
el.focus();
}
});
- 自定义局部指令:
directives: {
color: {
bind(el, binding) {
el.style.color = binding.value;
}
},
'font-weight': function (el, binding2) {
// 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数
el.style.fontWeight = binding2.value;
}
}
使用方式:
<input type="text" v-color="'red'" v-font-weight="900">
vue的生命周期
beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
运行期间的生命周期函数:
beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点
updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
销毁期间的生命周期函数:
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
vue-resource
- 发送get请求:
this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
console.log(res.body);
})
- 发送post请求:
this.$http.post(url,
{ name: 'zs' },
{ emulateJSON: true}).then(res => {
console.log(res.body);
});
全局配置接口和emulateJSON 选项:
- Vue.http.options.root = 'http://vue.studyit.io/';
注意:发起请求的前面不要加/了 this.$http.get('api/getprodlist').then
- Vue.http.options.emulateJSON = true;
Vue中的动画
- 使用过渡类名
v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入
v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了
v-enter-active 【入场动画的时间段】
v-leave-active 【离场动画的时间段】
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
<input type="button" value="toggle" @click="flag=!flag">
<transition>
<h3 v-if="flag">这是一个H3</h3>
</transition>
transition后可加name属性 修改动画的v-前缀
- 使用第三方 CSS 动画库
先导入动画类库:
<link rel="stylesheet" type="text/css" href="./lib/animate.css">
再定义 transition 及属性:
<transition
enter-active-class="fadeInRight"
leave-active-class="fadeOutRight"
:duration="{ enter: 500, leave: 800 }">
<div class="animated" v-show="isshow">动画哦</div>
</transition>
enter-active-class
leave-active-classs
:duration=''{enter: , leave: }'
animated
- 使用动画钩子函数
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div v-if="isshow" class="show">OK</div>
</transition>
定义三个 methods 钩子方法:
methods: {
beforeEnter(el) { // 动画进入之前的回调
el.style.transform = 'translateX(500px)';
},
enter(el, done) { // 动画进入完成时候的回调
el.offsetWidth;
el.style.transform = 'translateX(0px)';
done();
},
afterEnter(el) { // 动画进入完成之后的回调
this.isshow = !this.isshow;
}
}
- v-for 的列表过渡
transition-group 组件把v-for循环的列表包裹起来:
<transition-group appear tag="ul" name="list">
<li v-for="(item, i) in list" :key="i">{{item}}</li>
</transition-group>
transition-group 添加 appear 属性,实现页面刚展示出来时候,入场时候的效果
通过为 transition-group 元素,设置 tag 属性,指定 transition-group 渲染为指定的元素
下面的 .v-move 和 .v-leave-active 配合使用,能够实现列表后续的元素,渐渐地漂上来的效果
.v-move{
transition: all 0.8s ease;
}
.v-leave-active{
position: absolute;
}
vue组件
- 使用 Vue.extend 配合 Vue.component 方法:
var login = Vue.extend({
template: '<h1>登录</h1>'
});
Vue.component('login', login);
- 直接使用 Vue.component 方法:
Vue.component('register', {
template: '<h1>注册</h1>'
});
- 字面量方式
var login= {
template:#apl
data(){
return {}
}
}
<template id="apl">
<div></div>
</template>
vue.component('login', login)
//或者挂在到vm的components上
组件切换
v-if v-else
<input type="button" value="toggle" @click="flag=!flag">
<my-com1 v-if="flag"></my-com1>
<my-com2 v-else="flag"></my-com2>
component:is
使用component
标签,来引用组件,并通过:is
属性来指定要加载的组件:
注意:组件名称是字符串
<div id="app">
<a href="#" @click.prevent="comName='login'">登录</a>
<a href="#" @click.prevent="comName='register'">注册</a>
<hr>
<transition mode="out-in">
<component :is="comName"></component>
</transition>
</div>
父子组件传值
父向子传值
- 父组件定义子组件标签时bind简写绑定过去:
< son :finfo="msg"></ son>
再在子组件中定义props属性(注意是数组)
props: ['finfo']
子向父传值(借用父向字传递方法)
- 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
< son @func="getMsg"></ son>
- 子组件内部通过
this.$emit('方法名', 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用
this.$emit('func', data)
兄弟之间的传递
- 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
- 提供事件中心 var hub = new Vue()
- 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
- 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
- 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
<div id="app">
<div>父组件</div>
<div>
<button @click='handle'>销毁事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
兄弟组件之间数据传递
*/
//1、 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('tom-event', 1);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
//4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
地存储获取时需注意:
JSON.parse(localStorage.getItem(' cmts') || '[]')
localStorage.setItem('cmts', JSON.stringify(list))
组件插槽
- 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
匿名插槽
<div id="app">
<!-- 这里的所有组件标签中嵌套的内容会替换掉slot 如果不传值 则使用 slot 中的默认值 -->
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript">
/*
组件插槽:父组件向子组件传递内容
*/
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
# 当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”。
# 插槽内可以包含任何模板代码,包括 HTML
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
具名插槽
- 具有名字的插槽
- 使用 < slot >中的 "name" 属性绑定元素
<div id="app">
<base-layout>
<!-- 2、 通过slot属性来指定, 这个slot的值必须和下面slot组件得name值对应上
如果没有匹配到 则放到匿名的插槽中 -->
<p slot='header'>标题信息</p>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot='footer'>底部信息信息</p>
</base-layout>
<base-layout>
<!-- 注意点:template临时的包裹标签最终不会渲染到页面上 -->
<template slot='header'>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
具名插槽
*/
Vue.component('base-layout', {
template: `
<div>
<header>
### 1、 使用 <slot> 中的 "name" 属性绑定元素 指定当前插槽的名字
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
### 注意点:
### 具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序
<slot name='footer'></slot>
</footer>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
作用域插槽
- 父组件对子组件加工处理
- 既可以复用子组件的slot,又可以使slot内容不一致
<div id="app">
<!--
1、当我们希望li 的样式由外部使用组件的地方定义,因为可能有多种地方要使用该组件,
但样式希望不一样 这个时候我们需要使用作用域插槽
-->
<fruit-list :list='list'>
<!-- 2、 父组件中使用了<template>元素,而且包含scope="slotProps",
slotProps在这里只是临时变量
--->
<template slot-scope='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">
{{slotProps.info.name}}
</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</fruit-list>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
作用域插槽
*/
Vue.component('fruit-list', {
props: ['list'],
template: `
<div>
<li :key='item.id' v-for='item in list'>
### 3、 在子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法msg="xxx",
### 插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供了内容,会显示默认的内容。
如果父组件为这个插槽提供了内容,则默认的内容会被替换掉
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'orange'
},{
id: 3,
name: 'banana'
}]
}
});
</script>
</body>
</html>
购物车案例
this.$refs
定义:ref="mycom"
获取:this.$refs.mycom.innerText
vue-router
使用 router-link 组件来导航
< router-link to="/login">登录< /router-link>
使用 router-view 组件来显示匹配到的组件
< router-view>< /router-view>
创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
]
});
- 再挂载到vm实例上
路由传参
- restful风格:
const router = new VueRouter({
routes: [
{ path: '/register/:id', component: register }
]
})
通过this.$route.params获取
- url地址传参
<router-link to="/login?id=10&name=zs">登录</router-link>
通过this.$route.query.id获取
- props属性
const router = new VueRouter({
routes: [
{ path: '/register/:id', component: register, props: true}
]
})
const register = {
props: ['id']
template: `<div> {id} </div>`
}
路由嵌套
cost router = new VueRouter({
routes: [
{
path: '/account',·
component: account,
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
}
]
})
使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
命名路由
const router = new VueRouter({
routes: [
{ path: '/register/:id',
component: register,
name: register
}
]
})
使用:
<router-link :to="{name: 'register', params: {id: 123}}">
router.push({name: register, params: {id: 123}})
编程式导航
router.push('/home')
router.push({path: '/home'})
router.push({name: register, params: {id: 123}})
router.push({path: '/register', query: {uname: 'lisi'}})
命名视图
var router = new VueRouter({
routes: [
{
path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}
}
]
})
watch computed
watch
:
watch: {
'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据
this.fullName = newVal + ' - ' + this.lastName;
},
'$route': function (newVal, oldVal) {
if (newVal.path === '/login') {
console.log('这是登录组件');
}
}
computed
:computed: {
// 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发本计算 属性 的重新计算,从而更新 fullName 的值fullName() {
return this.firstName + ' - ' + this.lastName;
}
注意: 只要 计算属性,这个 function 内部,所用到的 任何 data 中的数据发送了变化,就会立即重新计算 这个 计算属性的值
计算属性缓存 vs 方法
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
webpack
安装
方式一:
- 运行
npm i webpack -g
全局安装webpack,这样就能在全局使用webpack的命令- 在项目根目录中运行
npm i webpack --save-dev
安装到项目依赖中
方式二:
安装webpack:
npm install webpack webpack-cli -D
执行命令:
1. webpack src/js/main.js dist/bundle.js
2. webpack
单独使用webpack
命令,webpack默认打包src目录里的index.js到dist目录的main.js
配置
在项目根目录中创建webpack.config.js
var path = require('path');
module.exports = {
entry: path.join(__dirname, 'src/js/main.js'),
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
}
}修改项目中的package.json文件添加运行脚本dev
"scripts":{
"dev":"webpack"
}运行dev命令进行项目打包:npm run dev
在页面中引入项目打包之后在dist文件夹中生成的js文件
<script src="../dist/bundle.js"></script>
webpack-dev-server
作用:webpack自动打包
npm i webpack-dev-server -D
在
package.json
文件中的指令,来进行运行webpack-dev-server
命令,在scripts
节点下新增:
"scripts":{
"dev":"webpack-dev-server"
}
此时需要修改index.html中script的src属性为:
<script src="/bundle.js"></script>
- 为了能在访问http://localhost:8080/的时候直接访问到index首页,可以使用--contentBase src指令来修改dev指令,指定启动的根目录:
"dev": "webpack-dev-server --contentBase src"
html-webpack-plugin
由于使用--contentBase
指令的过程比较繁琐,需要指定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin
插件配置启动页面.
- 运行
npm i html-webpack-plugin -D
安装到开发依赖 - 修改
webpack.config.js
配置文件如下:
var path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'src/js/main.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
plugins:[
new htmlWebpackPlugin({
template:path.join(__dirname, 'src/index.html'),
filename:'index.html'
})
]
}
- 修改
package.json
中script
节点中的dev指令如下:
"dev": "webpack-dev-server --open --host 127.0.0.1 --port 8888"
注意:html-webpack-plugin插件会自动把bundle.js注入到index.html页面中,所以可以不用添加引入bundle.js的script标签
webpack-dev-server配置项
方式1:
"dev": "webpack-dev-server --hot --port 4321 --open"
方式2:
- 修改
webpack.config.js
文件,新增devServer
节点如下:
devServer:{
hot:true,
open:true,
port:4321
}
- 在头部引入
webpack
模块:
var webpack = require('webpack');
- 在
plugins
节点下新增:
new webpack.HotModuleReplacementPlugin()
打包其他类型文件
- 安装相关打包需要工具
打包css、less、sass:
npm i style-loader css-loader -D
npm i less-loader less -D
npm i sass-loader node-sass -D
配置webpack.config.js的module中的rules数组:
module.exports = {
......
module : {
rules:[
{ test:/\.css$/, use:['style-loader','css-loader'] },
{ test:/\.less$/,use:['style-loader','css-loader','less-loader'] },
{ test:/\.scss$/,use:['style-loader','css-loader','sass-loader'] }
]
}
}
自动添加浏览器前缀postcss
npm i postcss-loader autoprefixer -D
创建并配置postcss.config.js文件:
const autoprefixer = require("autoprefixer");
module.exports = {
plugins:[ autoprefixer ]
}
配置更改webpack.config.js的module中的rules数组:
module.exports = {
......
plugins:[ htmlPlugin ],
module : {
rules:[
..........
{ test:/\.css$/, use:['style-loader','css-loader','postcss-loader'] }
]
}
}
处理css中的路径:
npm i url-loader file-loader -D
配置webpack.config.js的module中的rules数组:
module.exports = {
......
module : {
rules:[
.......
{ test:/\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
use:"url-loader?limit=16940" }
]
}
}
处理高级JS语法:
//转换器相关包
npm i babel-loader @babel/core @babel/runtime -D
//语法插件包
npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
在项目根目录创建并配置babel.config.js文件:
module.exports = {
presets:["@babel/preset-env"],
plugins:[ "@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties" ]
}
配置webpack.config.js的module中的rules数组:
rules:[
{ test:/\.js$/, use:"babel-loader", exclude:/node_modules/ }
]
处理vue:
npm i vue-loader vue-template-compiler -D
配置规则:更改webpack.config.js的module中的rules数组:
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
plugins:[ htmlPlugin, new VueLoaderPlugin()],
module : {
rules:[
......,
{ test:/\.vue$/,loader:"vue-loader",
}
]
}
}
构建Vue项目
npm install vue
- 在
webpack.config.js
中添加resolve
属性:
resolve: {
alias: {
'vue$': 'vue/dist/vue.js' //可以不添加
}
}
- 不修改,用 render()
//import vue from '../node_modules/vue/dist/vue.js'
import Vue from 'vue'
// 导入 App组件
import App from './components/App.vue'
// 创建一个 Vue 实例,使用 render 函数,渲染指定的组件
var vm = new Vue({
el: '#app',
render: c => c(App)
});
webpack完整配置:
var path = require('path')
var htmlWebpackPlugin = require('html-webpack-plugin')
var webpack = require('webpack')
module.exports = {
entry: path.join(__dirname, './src/main.js'), // 入口文件
output: { // 指定输出选项
path: path.join(__dirname, './dist'), // 输出路径
filename: 'bundle.js' // 指定输出文件的名称
},
plugins: [ // 所有webpack 插件的配置节点
new htmlWebpackPlugin({
template: path.join(__dirname, './src/index.html'), // 指定模板文件路径
filename: 'index.html' // 设置生成的内存页面的名称
}),
new VueLoaderPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: { // 配置所有第三方loader 模块的
rules: [ // 第三方模块的匹配规则
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=7631&name=[hash:8]-[name].[ext]' },
// limit 给定的值,是图片的大小,单位是 byte, 如果我们引用的 图片,大于或等于给定的 limit值,则不会被转为base64格式的字符串, 如果 图片小于给定的 limit 值,则会被转为 base64的字符串
{ test: /\.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }, // 处理 字体文件的 loader
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, // 配置 Babel 来转换高级的ES语法
{ test: /\.vue$/, use: 'vue-loader' }
]
},
resolve: {
alias: { // 修改 Vue 被导入时候的包的路径
// "vue$": "vue/dist/vue.js"
}
}
}
webpack中vue-router
- 下载
npm i vue-router
- main.js导入并引用
import VueRouter from 'vue-router'
Vue.use(VueRouter);
- 抽离router.js 导入需要展示的组件:
import login from './components/account/login.vue'
- 创建路由对象:
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
]
});
- 挂载到vue实例上
使用第三方组件
使用MintUI
首先下载:
npm i mint-ui
- 完整引入:
import MintUI from 'mint-ui' //导入所以组件
import 'mint-ui/lib/style.css' //可省略node_modules
Vue.use(MintUI)
//css组件可全局使用
<mt-button type="primary" size="large">primary</mt-button>
//组件中导入组件js组件要按需导入
import { Toast } from 'mint-ui'
//js中使用
this.toastInstance = Toast({
//配置项
})
//销毁Toast:
this.toastInstace.close()
- 按需导入MintUI
- 先下载:
npm install babel-plugin-component -D
然后将 .babelrc 修改为:
{
"presets": [
["es2015", { "modules": false }]
],
"plugins": [["component", [
{
"libraryName": "mint-ui",
"style": true
}
]]]
}
再按需导入:
import { Button, Cell } from 'mint-ui'
Vue.component(Button.name, Button)
Vue.component(Cell.name, Cell)
使用MUI
需手动下载,文档新建lib放入其中
import '../lib/mui/css/mui.min.css'
使用boostrap
首先:
npm install jquery -D
npm install bootstrap -D
webpack.config.js添加:
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"windows.jQuery": "jquery"
})
最后导入使用:
import $ from ‘jquery’
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/css/bootstrap.min.js'
中间组件的切换动画
v-enter和v-leave-to要拆分来写 还需加绝对定位:
.v-enter {
opacity: 0;
transform: translateX(100%);
}
.v-leave-to {
opacity: 0;
transform: translateX(-100%);
position: absolute;
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
vue中的vue-resource
import VueResource from 'vue-resource'
Vue.use(VueResource);
mui的tab-top-webview-main
分类滑动栏问题
App.vue 中的
router-link
身上的类名mui-tab-item
存在兼容性问题,导致tab栏失效,可以把mui-tab-item
改名为mui-tab-item1
,并复制相关的类样式,来解决这个问题;tab-top-webview-main`组件第一次显示到页面中的时候,无法被滑动的解决方案:
import mui from '../../../lib/mui/js/mui.min.js'
导入的 mui.js ,但是,控制台报错:
Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode
原因是 mui.js 中用到了 'caller', 'callee', and 'arguments' 东西,但是, webpack 打包好的 bundle.js 中,默认是启用严格模式的,所以,这两者冲突了;
解决方案: 1. 把 mui.js 中的 非严格 模式的代码改掉;但是不现实; 2. 把 webpack 打包时候的严格模式禁用掉;需要在webpack.config.js的plugings节点添加如下:
{ "plugins":["transform-remove-strict-mode"]
}
在 组件的 mounted
事件钩子中,注册 mui 的滚动事件:
mounted() {
mui('.mui-scroll-wrapper').scroll({
deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
});
}
- 滑动的时候报警告:
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
解决方法,可以加上 { touch-action: none; } 这句样式去掉。*
lazyload,vue-preview
1 mint-ui的lazyload按需导入有问题,只能全部导入所以组件
<ul>
<li v-for="item in list">
<img v-lazy="item">
</li>
</ul>
image[lazy=loading] {
width: 40px;
height: 300px;
margin: auto;
}
- vue-preview的使用:
<img class="preview-img" v-for="(item, index) in list" :src="item.src" height="100" @click="$preview.open(index, list)" :key="item.src">
getThumbs() {
// 获取缩略图
this.$http.get("api/getthumimages/" + this.id).then(result => {
if (result.body.status === 0) {
// 循环每个图片数据,补全图片的宽和高
result.body.message.forEach(item => {
item.w = 600;
item.h = 400;
});
// 把完整的数据保存到 list 中
this.list = result.body.message;
}
});
}
}
真机调试
要保证自己的手机可以正常运行;
要保证 手机 和 开发项目的电脑 处于同一个 WIFI 环境中,也就是说 手机 可以 访问到 电脑的 IP
打开自己的 项目中 package.json 文件,在 dev 脚本中,添加一个 --host 指令, 把 当前 电脑的 WIFI IP地址, 设置为 --host 的指令值;
- 如何查看自己电脑所处 WIFI 的IP呢, 在 cmd 终端中运行
ipconfig
, 查看 无线网的 ip 地址
vue-router编程式导航
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
抽离轮播图,小球动画,传值问题
抽离轮播图组件
<template>
<div>
<mt-swipe :auto="4000">
<!-- 在组件中,使用v-for循环的话,一定要使用key-->
<!-- 将来,谁使用此 轮播图组件,谁为我们传递 lunbotuList -->
<!-- 此时,lunbotuList 应该是 父组件向子组件传值来设置 -->
<mt-swipe-item v-for="item in lunbotuList" :key="item.url">
<img :src="item.img" alt="" :class="{'full': isfull}">
</mt-swipe-item>
</mt-swipe>
</div>
<!-- 1. 首页中的图片,它的宽和高,都是使用了100% 的宽度 -->
<!-- 2. 在商品详情页面中,轮播图的 图片,如果也使用 宽高为100%的话,页面不好看 -->
<!-- 3. 商品详情页面中的轮播图,期望 高度是 100%,但是 宽度为 自适应 -->
<!-- 5. 我们可以定义一个 属性,让使用轮播图的调用者手动指定是否为100%的宽度 -->
</template>
<script>
export default {
props: ["lunbotuList", "isfull"]
};
</script>
<style lang="scss" scoped>
.mint-swipe {
height: 200px;
.mint-swipe-item {
text-align: center;
img {
// width: 100%;
height: 100%;
}
}
}
.full {
width: 100%;
}
</style>
小球动画
beforeEnter(el) {
el.style.transform = "translate(0, 0)";
},
enter(el, done) {
el.offsetWidth;
// 小球动画优化思路:
// 1. 先分析导致 动画 不准确的 本质原因: 我们把 小球 最终 位移到的 位置,已经局限在了某一分辨率下的 滚动条未滚动的情况下;
// 2. 只要分辨率和 测试的时候不一样,或者 滚动条有一定的滚动距离之后, 问题就出现了;
// 3. 因此,我们经过分析,得到结论: 不能把 位置的 横纵坐标 直接写死了,而是应该 根据不同情况,动态计算这个坐标值;
// 4. 经过分析,得出解题思路: 先得到 徽标的 横纵 坐标,再得到 小球的 横纵坐标,然后 让 y 值 求差, x 值也求 差,得到 的结果,就是横纵坐标要位移的距离
// 5. 如何 获取 徽标和小球的 位置??? domObject.getBoundingClientRect()
// 获取小球的 在页面中的位置
const ballPosition = this.$refs.ball.-();
// 获取 徽标 在页面中的位置
const badgePosition = document
.getElementById("badge")
.getBoundingClientRect();
const xDist = badgePosition.left - ballPosition.left;
const yDist = badgePosition.top - ballPosition.top;
el.style.transform = `translate(${xDist}px, ${yDist}px)`;
el.style.transition = "all 0.5s cubic-bezier(.4,-0.3,1,.68)";
done();
},
afterEnter(el) {
this.ballFlag = !this.ballFlag;
}
numberbox子组件传值给父组件
//父组件:
getSelectedCount(count) {
// 当子组件把 选中的数量传递给父组件的时候,把选中的值保存到 data 上
this.selectedCount = count;
console.log("父组件拿到的数量值为: " + this.selectedCount);
}
//子组件:
countChanged() {
// 每当 文本框的数据被修改的时候,立即把 最新的数据,通过事件调用,传递给父组件
// console.log(this.$refs.numbox.value);
this.$emit("getcount", parseInt(this.$refs.numbox.value));
}
当父组件要传递给子组件的值是异步请求的值时需要子组件用watch去监听传递过来的值
父组件要传递给子组件的值是异步请求的值
watch: {
// 属性监听
max: function(newVal, oldVal) {
// 使用 JS API 设置 numbox 的最大值
mui(".mui-numbox")
.numbox()
.setOption("max", newVal);
}
}
vuex 优化购物车
加入购物车按钮:
addToShopCar() {
// 添加到购物车
this.ballFlag = !this.ballFlag;
// { id:商品的id, count: 要购买的数量, price: 商品的单价,selected: false }
// 拼接出一个,要保存到 store 中 car 数组里的 商品信息对象
var goodsinfo = {
id: this.id,
count: this.selectedCount,
price: this.goodsinfo.sell_price,
selected: true
};
// 调用 store 中的 mutations 来将商品加入购物车
this.$store.commit("addToCar", goodsinfo);
}
购物车vuex里的store:
var car = JSON.parse(localStorage.getItem('car') || '[]')
var store = new Vuex.Store({
state: { // this.$store.state.***
car: car // 将 购物车中的商品的数据,用一个数组存储起来,在 car 数组中,存储一些商品的对象, 咱们可以暂时将这个商品对象,设计成这个样子
// { id:商品的id, count: 要购买的数量, price: 商品的单价,selected: false }
},
mutations: { // this.$store.commit('方法的名称', '按需传递唯一的参数')
addToCar(state, goodsinfo) {
// 点击加入购物车,把商品信息,保存到 store 中的 car 上
// 分析:
// 1. 如果购物车中,之前就已经有这个对应的商品了,那么,只需要更新数量
// 2. 如果没有,则直接把 商品数据,push 到 car 中即可
// 假设 在购物车中,没有找到对应的商品
var flag = false
state.car.some(item => {
if (item.id == goodsinfo.id) {
item.count += parseInt(goodsinfo.count)
flag = true
return true
}
})
// 如果最终,循环完毕,得到的 flag 还是 false,则把商品数据直接 push 到 购物车中
if (!flag) {
state.car.push(goodsinfo)
}
// 当 更新 car 之后,把 car 数组,存储到 本地的 localStorage 中
localStorage.setItem('car', JSON.stringify(state.car))
},
updateGoodsInfo(state, goodsinfo) {
// 修改购物车中商品的数量值
// 分析:
state.car.some(item => {
if (item.id == goodsinfo.id) {
item.count = parseInt(goodsinfo.count)
return true
}
})
// 当修改完商品的数量,把最新的购物车数据,保存到 本地存储中
localStorage.setItem('car', JSON.stringify(state.car))
},
removeFormCar(state, id) {
// 根据Id,从store 中的购物车中删除对应的那条商品数据
state.car.some((item, i) => {
if (item.id == id) {
state.car.splice(i, 1)
return true;
}
})
// 将删除完毕后的,最新的购物车数据,同步到 本地存储中
localStorage.setItem('car', JSON.stringify(state.car))
},
updateGoodsSelected(state, info) {
state.car.some(item => {
if (item.id == info.id) {
item.selected = info.selected
}
})
// 把最新的 所有购物车商品的状态保存到 store 中去
localStorage.setItem('car', JSON.stringify(state.car))
}
},
getters: { // this.$store.getters.***
// 相当于 计算属性,也相当于 filters
getAllCount(state) {
var c = 0;
state.car.forEach(item => {
c += item.count
})
return c
},
getGoodsCount(state) {
var o = {}
state.car.forEach(item => {
o[item.id] = item.count
})
return o
},
getGoodsSelected(state) {
var o = {}
state.car.forEach(item => {
o[item.id] = item.selected
})
return o
},
getGoodsCountAndAmount(state) {
var o = {
count: 0, // 勾选的数量
amount: 0 // 勾选的总价
}
state.car.forEach(item => {
if (item.selected) {
o.count += item.count
o.amount += item.price * item.count
}
})
return o
}
}
})
vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。公用数据存储处理模块
- 使用步骤1:导包
import Vuex from 'vuex'
// 3. 注册vuex到vue中
Vue.use(Vuex)
- 新建store实例
var store = new Vuex.Store({
state: {
// 大家可以把 state 想象成 组件中的 data ,专门用来存储数据的
// 如果在 组件中,想要访问,store 中的数据,只能通过 this.$store.state.*** 来访问
count: 0
},
mutations: {
// 注意: 如果要操作 store 中的 state 值,只能通过 调用 mutations 提供的方法,才能操作对应的数据,不推荐直接操作 state 中的数据,因为 万一导致了数据的紊乱,不能快速定位到错误的原因,因为,每个组件都可能有操作数据的方法;
increment(state) {
state.count++
},
// 注意: 如果组件想要调用 mutations 中的方法,只能使用 this.$store.commit('方法名')
// 这种 调用 mutations 方法的格式,和 this.$emit('父组件中方法名')
subtract(state, obj) {
// 注意: mutations 的 函数参数列表中,最多支持两个参数,其中,参数1: 是 state 状态; 参数2: 通过 commit 提交过来的参数;
console.log(obj)
state.count -= (obj.c + obj.d)
}
},
getters: {
// 注意:这里的 getters, 只负责 对外提供数据,不负责 修改数据,如果想要修改 state 中的数据,请 去找 mutations
optCount: function (state) {
return '当前最新的count值是:' + state.count
}
// 经过咱们回顾对比,发现 getters 中的方法, 和组件中的过滤器比较类似,因为 过滤器和 getters 都没有修改原数据, 都是把原数据做了一层包装,提供给了 调用者;
// 其次, getters 也和 computed 比较像, 只要 state 中的数据发生变化了,那么,如果 getters 正好也引用了这个数据,那么 就会立即触发 getters 的重新求值;
}
})
state
- *
mapState
辅助函数
- 某个组件想要用到store里state里的数据时,
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
2. 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState
传一个字符串数组。
computed: mapState(['count'])
// 映射 this.count 为 store.state.count
- 当与局部计算属性混合使用:
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
getters
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
通过属性访问:
store.getters.doneTodos
Getter 也可以接受其他 getter 作为第二个参数:
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mapGetters辅助函数:
return方法的gettersb不能用mapGetters映射
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
]),
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
}
mutation
- 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
需要以相应的 type 调用 store.commit 方法:
store.commit('increment')
- 提交载荷(Payload):
你可以向 store.commit
传入额外的参数,即 mutation 的 载荷(payload):
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
- 对象风格的提交方式
store.commit({
type: 'increment',
amount: 10
})
Mutation 必须是同步函数
mapMutations
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
incrementByMutation (state,playLoad) {
state.count + = playload.count
}
},
actions: {
incrementByAction (context) {
context.commit('incrementByMutation')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。
实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit
很多次的时候):
actions: {
incrementByAction ({ commit }) {
commit('incrementByMutation')
}
}
Action 通过 store.dispatch
方法触发:
store.dispatch('incrementByAction')
可以在 action 内部执行异步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('incrementByMutation')
}, 1000)
}
}
Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
actions: {
incrementAsync ({ commit },count) {
setTimeout(() => {
commit('incrementByMutation',count)
}, 1000)
}
}
组合action:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
module
使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
- 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
- 同样,对于模块内部的 action,局部状态通过 context.state暴露出来,根节点状态则为 context.rootState:
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
- 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
无命名空间情况下,模块内部的 getter、mutation 和 getter 是注册在全局命名空间下的,所以:
- 访问a模块下count的state:
store.state.a.count
- 访问a模块下的mutation ,getter,action
store.commit('someMutationInA')
store.getters.someGetterINA
store.dispatch('someActionINA')
添加命名空间:
- 访问a模块下的mutation ,getter,action:
store.commit('a/someMutationInA')
store.getters['a/someGetterINA']
store.dispatch('a/someActionINA')
- 在带命名空间的模块内访问全局内容:
modules: {
foo: {
namespaced: true,
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
- 带命名空间的模块注册全局 action
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
- 带命名空间的绑定函数
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
},
methods: {
...mapActions([
'some/nested/module/foo', // -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()
])
}
简化:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
而且,你可以通过使用 createNamespacedHelpers
创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo',
'bar'
])
}
}
Vue笔记(有点乱)的更多相关文章
- SQL反模式学习笔记2 乱穿马路
程序员通常使用逗号分隔的列表来避免在多对多的关系中创建交叉表, 将这种设计方式定义为一种反模式,称为“乱穿马路”. 目标: 存储多属性值,即多对一 反模式:将多个值以格式化的逗号分隔存储在一个字段中 ...
- Vue笔记目录
Vue笔记目录 一.Vue.js介绍 二.vue基础-vue-cli(vue脚手架) ...持续更新中,敬请期待
- 《Vue笔记01: 我与唐金州二三事》
最近我在收看唐金州在极客时间发布的<vue从入门到精通>,颇有收获. 唐金州,一点资讯前端技术专家,曾在蚂蚁金服就职,也是开源组件库ant design vue的作者,虽然唐老师写的ant ...
- Vue笔记--通过自定义指令实现按钮操作权限
经常做中后台系统,此类系统的权限是比较重要,拿自己做过的一些项目做个笔记. Vue实现的中后台管理系统.按钮操作权限的空置一般都是通过自定义指令Vue.directive. <el-button ...
- 一个后端开发的 Vue 笔记【入门级】
一 前言 最近找了些教程,顺带着趴在官网上,看了看 Vue 的一些内容,入门的一些概念,以及基础语法,还有一些常用的操作,瞄了一眼,通篇文字+贴了部分代码 9000 多字,入门语法什么的还是很好理解的 ...
- vue笔记
安装vue脚手架工具 sudo cnpm install -g vue-cli
- vue笔记 递归组件的使用
递归组件 什么是递归组件? 组件自身去调用组件自身. 代码示例 DetailList.vue(子组件-递归组件) <template> <div> <div class= ...
- vue笔记-列表渲染
用v-for把一个数组对应为一组元素 使用方法:v-for="(item,index) in items"//也可以使用of替代in { items:源数组 item:数组元素迭代 ...
- vue笔记-条件渲染
条件渲染 1:指令v-if单独使用和结合v-else //单独使用 <h1 v-if="ok">Yes</h1> //组合使用 <h1 v-if=&q ...
随机推荐
- 洛谷 P4910 帕秋莉的手环
题意 多组数据,给出一个环,要求不能有连续的\(1\),求出满足条件的方案数 \(1\le T \le 10, 1\le n \le 10^{18}\) 思路 20pts 暴力枚举(不会写 60pts ...
- Newbe.Claptrap 框架入门,第一步 —— 创建项目,实现简易购物车
让我们来实现一个简单的 “电商购物车” 需求来了解一下如何使用 Newbe.Claptrap 进行开发. 业务需求 实现一个简单的 “电商购物车” 需求,这里实现几个简单的业务: 获取当前购物车中的商 ...
- Oracle基础介绍及常用相关sql*plus命令
以管理员身份运行Database Configuration Assistant,新建数据库实例. 要使用Oracle首先要启动Oracle服务,在任务管理器中找到服务,打开有关OracleServi ...
- scrapy 基础组件专题(八):scrapy-redis 框架分析
scrapy-redis简介 scrapy-redis是scrapy框架基于redis数据库的组件,用于scrapy项目的分布式开发和部署. 有如下特征: 分布式爬取 您可以启动多个spider工 ...
- 数据可视化之分析篇(九)PowerBI数据分析实践第三弹 | 趋势分析法
https://zhuanlan.zhihu.com/p/133484654 以财务报表分析为例,介绍通用的分析方法论,整体架构如下图所示: (点击查看大图) 我会围绕这五种不同的方法论,逐步阐述他们 ...
- 前端02 /HTML标签
前端02 /HTML标签 目录 前端02 /HTML标签 1.特殊字符 2.标签分类 标签嵌套 1.块级标签(行内标签) 1.1div标签(块标签) 1.2p标签(块标签) 2.内联标签 2.1 sp ...
- MySQL数据库使用报错ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
今天MySQL数据库,在使用的过程中一直报错ERROR 1820 (HY000): You must reset your password using ALTER USER statement be ...
- C++中类继承public,protected和private关键字作用详解及派生类的访问权限
注意:本文有时候会用Visual Studio Code里插件的自动补全功能来展示访问权限的范围(当且仅当自动补全范围等价于对象访问权限范围的时候),但是不代表只要是出现在自动补全范围内的可调用对象/ ...
- Java匿名对象介绍
Java匿名对象介绍 什么是匿名对象? 顾名思义就是没有变量名的对象,即创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量. 匿名对象命名格式:以Scanner类举例 new Scann ...
- Flink之对时间的处理
window+trigger+watermark处理全局乱序数据,指定窗口上的allowedLateness可以处理特定窗口操作的局部事件时间乱序数据 1.流处理系统中的微批 Flink内部也使用了某 ...