目录
[v-cloak] {
display: none;
}
<div v-text="msg2"></div>
<div v-html="msg2">1212112</div>
<input type="button" value="按钮" :title="mytitle + '123'">
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-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="['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="(item, i) in list
v-for="(val, key, i) in userInfo
v-for="i in 10"
当在组件中使用** v-for 时,key 现在是必须的。
v-if 的特点:每次都会重新删除或创建元素
v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式
如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show,如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if
<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">
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">
mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
运行期间的生命周期函数:
updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
销毁期间的生命周期函数:
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
console.log(res.body);
})
this.$http.post(url,
{ name: 'zs' },
{ emulateJSON: true}).then(res => {
console.log(res.body);
});
全局配置接口和emulateJSON 选项:
注意:发起请求的前面不要加/了 this.$http.get(‘api/getprodlist‘).then
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-前缀
先导入动画类库:
<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;
}
}
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;
}
var login = Vue.extend({
template: '<h1>登录</h1>'
});
Vue.component('login', login);
Vue.component('register', {
template: '<h1>注册</h1>'
});
var login= {
template:#apl
data(){
return {}
}
}
<template id="apl">
<div></div>
</template>
vue.component('login', login)
//或者挂在到vm的components上
<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>
父向子传值
< son :finfo="msg"></ son>
再在子组件中定义props属性(注意是数组)
props: ['finfo']
子向父传值(借用父向字传递方法)
< son @func="getMsg"></ son>
this.$emit(‘方法名‘, 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用this.$emit('func', data)
<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>
<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>
<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>
定义:ref="mycom"
获取:this.$refs.mycom.innerText
使用 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 }
]
});
const router = new VueRouter({
routes: [
{ path: '/register/:id', component: register }
]
})
通过this.$route.params获取
<router-link to="/login?id=10&name=zs">登录</router-link>
通过this.$route.query.id获取
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
: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 中的数据发送了变化,就会立即重新计算 这个 计算属性的值
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
方式一:
- 运行
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自动打包
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>
"dev": "webpack-dev-server --contentBase src"
由于使用--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标签
方式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()
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'] }
]
}
}
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'] }
]
}
}
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" }
]
}
}
//转换器相关包
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/ }
]
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",
}
]
}
}
npm install vue
webpack.config.js
中添加resolve
属性:resolve: {
alias: {
'vue$': 'vue/dist/vue.js' //可以不添加
}
}
//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)
});
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"
}
}
}
npm i vue-router
import VueRouter from 'vue-router'
Vue.use(VueRouter);
import login from './components/account/login.vue'
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
]
});
首先下载:
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()
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)
需手动下载,文档新建lib放入其中
import '../lib/mui/css/mui.min.css'
首先:
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;
}
import VueResource from 'vue-resource'
Vue.use(VueResource);
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; } 这句样式去掉。
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;
}
<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 的指令值;
ipconfig
, 查看 无线网的 ip 地址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;
}
//父组件:
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: {
// 属性监听
max: function(newVal, oldVal) {
// 使用 JS API 设置 numbox 的最大值
mui(".mui-numbox")
.numbox()
.setOption("max", newVal);
}
}
加入购物车按钮:
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 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。公用数据存储处理模块
import Vuex from 'vuex'
// 3. 注册vuex到vue中
Vue.use(Vuex)
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 的重新求值;
}
})
mapState
辅助函数*// 在单独构建的版本中辅助函数为 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({
// ...
})
}
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)
}
}
}
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
需要以相应的 type 调用 store.commit 方法:
store.commit('increment')
你可以向 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 类似于 mutation,不同在于:
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')
})
}
}
使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,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 的状态
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
无命名空间情况下,模块内部的 getter、mutation 和 getter 是注册在全局命名空间下的,所以:
store.state.a.count
store.commit('someMutationInA')
store.getters.someGetterINA
store.dispatch('someActionINA')
添加命名空间:
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) { ... }
}
}
}
{
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'
])
}
}
原文:https://www.cnblogs.com/garyxi/p/12247020.html