VUE学习

css:https://www.w3school.com.cn/cssref/pr_dim_line-height.asp

一、邂逅Vuejs

1.Vue简介 

  1. Vue是一个渐进式的框架:一点点重构;核心库以及生态系统;
  2. 有很多特点和web开发中常见的高级功能:可复用组件;

2.Vue.js安装

1.直接CDN引入:

开发环境:<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

生产环境:<script src="https://cdn.jsdelivr.net/npm/vue"></script>

2.下载和引用:

开发环境:https://vuejs.org/js/vue.js

生产环境:https://vuejs.org/js/vue.min.js

NPM安装:

3.Vuejs初体验

01-HelloVuejs.html:着重new app({})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app"><h2>{{message}}</h2></div>
<script src="../js.vue/js"></script>
<script>
    //let(变量)/const(常量)
    //编程范式:声明式编程;js:命令式编程
    const app=new Vue({
        el:'#app',//用于挂载要管理的元素
        data:{//定义数据
            message:'你好啊,李银河!'
        }
    })
</script>
</body>
</html>

02-vue列表展示:着重v-for

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="item in movies">{{item}}</li>
    </ul>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            message:'你好啊',
            movies:['星际穿越','大话西游','少年派','盗梦空间']
        }
    })
</script>
</body>
</html>

案例3:计数器:着重methods; v-on:click="(方法名)"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
    <div id="app">
        <h2>当前计数:{{counter}}</h2>
        <button v-on:click="add">+</button>
        <button v-on:click="sub">-</button>
    </div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            counter:0
        },
        methods:{
            add:function(){
                console.log('add被执行');
                this.counter++
            },
            sub:function(){
                console.log('sub被执行');
                this.coumter--
            }
        }
    })
</script>
</body>
</html>

4.Vue中的MVVM

计数器中的MVVM:

View是我们的DOM,在网页上展示出来的

Mode是我们抽离出来的obj

ViewMode是我们创建的Vue对象实例

ViewModel通过Data Bingging 让obj中的数据实时在DOM中显示

ViewModel通过DOM Listener来监听DOM事件,并通过methods中的操作,来改变OBJ中的数据。

5. Vue的生命周期

github->vue->tag版本

new Vue({el:'',data:,methods:{},beforeCreate:function(){},created:function(){},mounted:function(){}})

6.Vue的template

定义模板

二、插值语法

mustache语法: <h2>{{message+' '+firstname}}</h2>

v-once:<h2 v-once>{{message}}</h2> ,message不改变

v-html : <h2 v-html='link'></h2>,按照html格式进行解析

v-text: <h2 v-text="message"></h2> 中间接数据也会被覆盖

v-pre: <p v-pre>{{message}}</p> 跳过编译过程,直接显示{{message}}

v-cloak:防止Vue还没来得及解析,显示出不一样的数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
    <style>
        [v-cloke]{
            display:none;
        }
    </style>
</head>
<body>
   <div id="app" v-cloke>{{message}}</div>
<script src="../js/vue.js"></script>
<script>
    setTimeout(function(){
        const app=new Vue({
            el:'#app',
            data:{
                message:'你好啊'
            }
        })
    },1000)
</script>
</body>
</html>

三、动态绑定属性v-bind

v-bind:  <img v-bind:src="imgURL" alt=""><a v-bind:href="aHref">百度一下</a> :动态改标签

1.动态绑定class

通过对象

<h2:class="{'active':isActive}">Hello World</h2>

<h2:class="{'active':isActive,'line':isLine}">Hello World</h2>

<h2 class="tittle" :class="{'active':isActive,'line':isLine}">Hello World</h2>

<h2 class="tittle" :class="classes">Hello World</h2>

绑定class 通过数组

<h2 class="tittle" :class="[active,line]">{{message}}</h2>

v-bind语法糖:<img :src="imgURL" alt="">

<!DOCTYPE>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
    <style>
        .active{
            color:red;
        }
   </style>
</head>
<body>
<div id="app">
    //<h2 v-bind:class="{active:isActive,line:isLine}">{{message}}</h2>
    <h2 class="tittle" v-bind:class="getClasses()">{{message}}</h2>
    <button v-on:click="btnClick">按钮</button>
</div>
<script src="../vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            message:'你好啊',
            isActiove:true,
            isLine:true
        },
        methods:{
            btnClick:function(){
                this.isActive=!this.isActive
            },
            getClasses:function(){
                return{active:this.isActive,line:this.isLine}
            }
        }
    })
</script>
</body>
</html>

2.动态绑定style

1:对象语法

<!DOCTYPE>
<html lang="en">
<head>
    <meta charst="UTF-8">
    <tittle>Tlttle</tittle>
</head>
<body>
<div id="app">
    <h2 :style="{fontSize:finalSize + 'px',backgroundColor:finalColor}">{{message}}</h2>
<div>
<script src="../js/vue.js"></script>
<script>
     const app=new Vue({
        el:'#app',
        data:{
            message:'你好啊',
            finalSize:100,
            finalColor:'red'
        }
    })
</script>
</body>
</html>

v-bind绑定style2:数组语法

<div v-bind:style="[baseStyles,overridingStyles]"></div>

四.计算属性

结合之后再显示

1.案例1

data:{computed:{fullName:function(){return this.firstName+' '+this.lastName}}}

<!DCOTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <h2>总价格:{{totalPrice}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            books:[
                {id:110,name:'Unix编程艺术',price:119},
                {id:111,name:'代码大全',price:105},
                {id:112,name:'深入理解计算机原理',price:98},
                {id:113,name:'现代操作系统',price:87},
            ]
        },
        computed:{
            totalPrice:function(){
                let result=0
                    for(let i=0;i<this.books.length;i++){
                        result += this.books[i].price
                    }
                 return result
        }
    })
</script>
</body>
</html>

2.setting和getting

<!DOCTYPE html>
<html>
<head>
    <meta charst="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id='app'>{{fullName}}</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            firstName:'Kobe',
            lastName:'Bryant'
        },
        computed:{
        //计算属性一般只有set方法,只读属性,所以可以简写:fullName:function(){}
            fullName:{
                set:function(newValue){
                    const names=newValue.split('');
                    this.firstName=names[0];
                    this.lastName=names[1];
                },
                get:function(){
                    return this.firstName+' '+this.lastName
                }
        }
    }
</script>
</body>
</html>

计算属性和methods对比:

计算属性:fullName;

methods: get FullName(sea(),get());

methods是每次都执行,而计算属性是会把数据缓存的,所以一般使用计算属性

3.块级作用域-let和var

es5之前if和for都没有会计作用域的概念,所以很多时候我们必须借助于function的作用域来解决应用外面变量的问题

es6中,加入了let

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charst="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>

<script>
    /*
    var btns=document.getElementsByTagName('button');
    for(var i=0;i<btns.length;i++){
        btns.addEventListener('click',function(){
            console.log('第'+num+'个按钮被点击');
        })
    })(i)
    }

    */
    const btns=document.getElementsByTagName('button')
    for(let i=0;i<btns.length;i++){
        btns[i].addEventListener('click',function(){
        console.log('第'+i+'个按钮被点击');
        })
    }
</script>
</body>
</html>
    

4.const的使用和注意点

注意1:一旦给const修师的标识符被赋值之后,不能被修改

const name='why';

name='abc';

注意2:在使用const定义标识符,必须进行赋值

const name;

注意3:常量的含义是指向的对象不能修改,但是可以改变对象内部的属性

const obj={

    name:'why',

    age: 18,

    height: 1.88

}

obj.name='kobe';

5.对象增强写法

1.属性的简写:

//ES6之前

let name='why'

let age=18

let obj1={

    name:name,

    age: age

}

//ES6之后

let obj2={

    name,age

}

2.方法的简写

//ES6之前

let obj1={

    test:function(){
        console.log('obj1的test函数');

    }

}

//ES6之后

let obj2={

    test(){
        console.log('obj2的test函数')

    }

}

obj2.test()

五.事件监听:v-on

<button v-on:click="increment">+</button>

语法糖:

<button @click="increment">-<button>

1.参数:

情况1:如果该方法不需要额外参数,那么方法后的()可以不添加。

如果方法中有一个参数,那么会默认将原生事件event参数传递过去:undefined

情况2:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。

2.修饰符:

1、停止冒泡:

<button @click.stop="btnClick">按钮</button>

2、阻止默认行为

<input type="submit" value="提交" @click.prevent="submitClick">

<button @click.prevent="doThis"></button>

3、键盘修饰符

<input type="text" @keyup.enter="keyUp">

4、点击回调只会触发一次

<button @click.once="btn">按钮2</button>

六:条件判断

1.v-if,v-else-if,v-else

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <h2 v-if="isshow">
        <div>abc</div>
        {{message}}
    </h2>
    <h1 v-else>isShow为false时,显示我</h1>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=newVue({
        el:'#app',
        data:{
            message:'你好啊',
            isShow:true
        }
    })
</script>
</body>
</html>

v-if和v-show对比:

v-if当条件为false时,压根不会有对应的元素在DOM中

v-show当条件为false时,仅仅是将元素的display属性设置为none

当需要在显示与隐藏之间切换很频繁时,使用v-show

当只有一次切换时,通常使用v-if

一般使用v-if

2.登录切换小案例

加key 不复用,placeholder 添加文本

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <span v-if="isUser">
        <label for="username">用户账号</label>
        <input type="text" id="username" placeholder="用户账号" key="username">
    </span>
    <span v-else>
        <label for ="email">用户邮箱</label>
        <input type="text" id="email" placeholder="用户邮箱" key="email">
    </span>
    <button @click="isUser=!isUser">切换类型</button>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            issUser:true
        }
    })
</script>
</body>
</html>

七.v-for遍历数组和对象

1.遍历数组:

<li v-for="item in names">{{item}}</li>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <ul>
<!--在遍历过程中,没有使用索引值(下标值)-->
        <li v-for="item in names">{{item}}</li>
    </ul>
    <ul>
<!--//在遍历过程中,获取索引值-->
        <li v-for="{item,index} in names">{{index+1}}.{{item}}</li>
    </ul>
</div>
<script src="../js/vue.js></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            name:['why','kobe','james','curry']
        }
    )}
</script>
</body>
</html>

2.遍历对象:

<li v-for="(value,key,index) in info">{{value}}-{{key}}-{{index}}</li>
    1.  只获取value: <li v-for="item in info">{{item}}</li>
     2.  获取key和value: <li v-for="(value,key) in info"{{value}}-{{key}}</li>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <ul>
   
        <li v-for="(value,key,index)">{{value}}-{{key}}-{{index}}</li>
    </ul>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            info:{
                name:'why',
                age:'18',
                height:'1.88'
            }
        }
    })
</script>
</body>
</html>

绑定key:

<li v-for="item inletters" :key="item">{{item}}</li>

如果不绑定,默认先插入,后面的元素再位移,效率很低

3.数组的响应式方法

可变长函数:

sun:functuin(...num){

console.log(num)l

}

push也是可变长函数

响应式方法:

1.push:在最后追加元素:this.letters.push('aaa','bbb','ccc)

2.pop: 删除数组中最后一个元素:this.letter.pop();

3.shift():删除数组中第一个元素: this.letter.shift()

4.unshift():在数组最钱买你添加元素:this.letters.unshift('aaa','bbb','ccc')

5.splice:删除、插入、替换元素

this.letters.splice(1,3,'m','n','1','x') 替换元素

this.letters.splice(1,0,'x','y','z') 插入元素

5.sort()排列:this.letter.sort()

6.reverse() 反转: this.letters.reverse()

注意:

通过索引值修改数组中的元素,不是响应式的
this.letters[0]='bbb'

可以使用splice:

this.letters.splice[0,1,'bbb']

可以用vue.set()

Vue.set(this.letters,0,'bbb')

4.电影点击案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
    <style>
        .active{
            color:red;
        }
    </style>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(item,index) in movies" :class="{active:currentIndex===index}">{{index}}.{{index}}
        </li>
    </ul>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'app',
        data:{
            movies:['海王','海贼王','加勒比海盗','海尔兄弟'],
            currentIndex:0
        },
        methods:{
            liClick(index){
                this.currentIndex=index
            }
        }
    })
</script>
</body>
</html>

5.购物车案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app">
    <div v-if="books.length">
    <table>
    <thead>
    <tr>
        <th></th>
        <th>书籍名称</th>
        <th>出版日期</th>
        <th>价格</th>
        <th>购买数量</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    <tr v-for="item in books">
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
        <td>{{item.date}}</td>
<!--过滤器-->
        <td>{{item.price|showPrice}}</td>
        <td>
            <button @clicck="decrement(index)" v-bind:disabled="item.count<=1">-</button>
            {{item.count}}
            <button @click="increment(index)">+</button>
        <td><button @click="removeHandle(index)">移除</button></td>
    <tr>
    </tbody>
    </table>
    <h2>总价格:{{totalPrice|showPrice}}</h2>
    </div>
    <h2 v-else>购物车为空</h2>
</div>
<script src="../js/vue.js"></script>
<script src="main.js></script>
</body>
</html>
    
const app=new Vue({
    el:'app',
    data:{
        books:[{price:'18,name:'数据库',data:'2020-11'},{price:'18,name:'数据库',data:'2020-11'},{price:'18,name:'数据库',data:'2020-11'},{price:'18,name:'数据库',data:'2020-11'}]
    },
    methods:{
        increment(index){
            this.books[index].count++
        },
        decrement(index){
            this.books[index].count--
        },
        removeHandle(index){
            this.books.splice(index,1)
        }
    },
    computed:{
        totalPrice(){
            let totalPrice=0
            for(let i=0;i<this.books.length;i++){
                total+=this.books[i].price*this.books[i].count
            }
            return totalPrice
        }
    },
    filters:{
        showPrice(price){
            return '¥'+price.toFixed(2)
        }
    }
})

total的高阶函数编程 filter/map/reduce

const nums=[10,20,111,222,444]

let total=nums.filter(function(n){

  return n<100

}).map(function(n){

  return n*2

}).reduce(function(prevValue,n){

  return prevValue+n

},0)

console.log(total)

更简洁的写法:

let total=nums.filter(n=>n<100).map(n=>n*2).reduce((pre,n)=>pre+n);
 

八.表单绑定v-model

使用v-model指令实现表单元素和数据的双向绑定

<div id="app">
    <input type="text" v-model="message">
    <h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
    let app=new Vue({
        el:'#app',
        data:{
            message:''
        }
    })
</script>

可以将v-model用于textarea元素

<textarea v-model="message"></textarea>
<p>输入的内容是:{{message}}</P>

v-model实际是一个语法糖,他的本质是:

<input type="text" v-model="message">
<!--等同于-->
<input type="text" v-bind:value="message" v-on:input="message=$event.target.value">

v-model结合radio使用:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
    <label for="male">
<!--如果没有v-model要使用name才会只选一个,label:可以点文字-->
        <input type="radio" id="male" value="男" v-model="sex">男
    </label>
    <label for="female">
        <input type="radio" id="female" value="女" v-model="sex">女
    </label>
    <h2>您选择的性别是: {{sex}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
          message:'你好啊',
          sex:'女'
        }
    })
</script>
</body>
</html>  

单选框:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
    <label for="agree">
        <input type="checkbox" id="agree" v-model="isAgree">同意协议
    </label>
    <h2>您的选择是:{{isAgree}}</h2>
    <button :disabled="!isAgree">下一步</button>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            isAgree:'false'
        }    
    })
</script>
</body>
</html>

多选框

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
    <input type="checkbox" value="篮球" v-model="hobbies">篮球
    <input type="checkbox" value="足球" v-model="hobbies">足球
    <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
    <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
    <h2>您的爱好是:{{hobbies}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            hobbies:[]
        }
    })
</script>
</body>
</html>

下拉框:select

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
<!--选择1个-->
    <select name="abc" v-model="fruit">
        <option value="苹果">苹果</option>
        <option value="香蕉">香蕉</option>
        <option value="榴莲">榴莲</option>
        <option value="葡萄">葡萄</option>
    </select>
    <h2>您选择的水果是:{{fruit}}</h2>
<!--选择2个-->
    <select name="abc" v-model="fruits"multiple>
        <option value="苹果">苹果</option>
        <option value="香蕉">香蕉</option>
        <option value="榴莲">榴莲</option>
        <option value="葡萄">葡萄</option>
    </select>
</div>
<script src="../js/vue.js"></script>
<script>
    const spp=newVue({
        el:'#app',
        data:{
            fruit:'香蕉',
            fruits:[]
        }
    })
</script>
</body>
</html>

值绑定

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
    <label v-for="item in originHobbies" :fro="item>
        <input type="checkbox" :value="item" :id="item" v-model="nobbies">{{item}}
    </label>
</div>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            originHobbies:['篮球','足球','乒乓球','台球','高尔夫球']
        }
    })
</script>
</body>
</html>

修饰符

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
<!--1.修饰符:lazy: 失去焦点或者回车才会model变化-->
    <input type="text" v-model.lazy="message">
    <h2>{{message}}</h2>
<!--2.修饰符: number : 不会让number转换为string类型-->
    <input type="number" v-model.number="age">
    <h2>{{age}}--{{typeof age}}</h2>
<!--3.修饰符:trim: 去除两边的空格-->
    <input type="text" v-model.trim.="name">
    <h2> 您输入的名字:{{name}}</h2>


<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            message:'你好啊',
            age:0
            name:''
        }
    })
</script>
</body>
</html>

九.组件化component

任何应用都会被抽象成一棵组件树

步骤:创建组件构造器、注册组件、使用组件

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
<!--3.使用组件-->
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
    <my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
<!--1.创建组件构造器对象-->
    const cpnC=Vue.extend({
        template:`
            <div>
                <h2>我是标题</h2>
                <p>我是内容,哈哈哈</p>
                <p>我是内容,呵呵呵</p>
            </div>`
    })

<!--2.注册组件-->
    Vue.component('my-cpn',cpnC)
</script>
</body>
</html>

局部组件:

const app=new Vue({

    el:'#app',

    components:{

        cpn:cpnc

    }

})

父子组件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<div id="app">
    <cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
<!--1.创建第一个组件构造器-->
    const cpnC1=Vue.extend({
        template:`
            <div>
                <h2>我是标题1</h2>
                <p>我是内容,哈哈哈</p>
            </div>
        `
    })
<!--2.创建第二个组件构造器(父组件)-->
    const cpnC2=Vue.extend({
        template:`
            <div>
                <h2>我是标题2</h2>
                <p>我是内容,呵呵呵</p>
            <div>
        `,
        components:{
            cpn1:cpnC1
        }
    )}
<!--root组件-->
    const app=new Vue({
        el:'#app',
        components:{
            cpn2:cpnC2
        }
    })
</script>
</body>
</html>

注册组件语法糖

<!--1.全局-->
Vue.component('cpn1',{
    template:`
                <div>
                    <h2>我是标题2</h2>
                    <p>我是内容,呵呵呵</p>
                </div>
            `
})
        

<!--2.局部-->
const app=new Vue({
    el:'#app',
    components:{
        'cpn2':{
            template:`
                <div>
                    <h2>我是标题2</h2>
                    <p>我是内容,呵呵呵</p>
                </div>
            `
        }
    }
})

模板的分离写法

<!-- 使用script-->
<script type="text/x-template" id="myCpn">
    <div>
        <h2>组件标题</h2>
        <p>我是组件的内容</p>
    </div>
</script>
<script src="../js/vue.js"></script>
<script>
    let app=new Vue({
        el:'#app',
        components:{
            'my-cpn':{
                template:'#myCpn'
            }
        }
    })
</script>

<!-- 使用template-->
<template id="myCpn">
    <div>
        <h2>组件标题</h2>
        <p>我是组件的内容</p>
    </div>
</template>
<script>
    let app=new Vue({
        el:'#app',
        components:{
            'my-cpn':{
                template:'#myCpn'
            }
        }
    })
</script>

在vue定义tittle,注册组件是用不到的

组件的data必须使用函数,让调用者都有自己独立的数据,不然每一次调用指向的都是同一个对象地址,会发生连锁反应

<template id="cpn">
<div>
    <h2>当前计数:{{counter}}</h2>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn',{
    template:'#cpn',
    data(){
        return{
            counter:0
        },
        methods:{
            increment(){
                this.counter++
            },
            decrement(){
                this.counter--
            }
        }
})
</script>

父子组件的通信

1.通过props向子组件传递数据

2.通过事件向父组件发送信息

props:{
1.类型限制
cmovies:Array
cmessage:String
2.提供一些默认值,以及必传值
cmessage:{
    type:String,
    default:'aaa',
    required:true
},
3.类型是对象或者数组时,默认值必须是一个函数
cmovies:{
     type:Array,
    default(){
        return[]
    }
}
}

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
</head>
<body>
<div id="app">
    <cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
    <div>
        <ul>
            <li v-for="item in cmovies">{{item}}</li>
        </ul>
        <h2>{{cmessage}}</h2>
    </div>
</template>
<script src='../js/vue.js'></script>
<script>
    const cpn={
        template:'#cpn',
        props:{['cmovies','cmessage']},
        data(){
            return []
        },
        methods{}
    }
    const app=new Vue({
        el:'#app',
        data:{
            movies:['haiwang','海贼王','海尔兄弟'],
            message:'你好啊'
        },
        components:{
            cpn
        }
    })
</script>
</body>
</html>

父子组件通信-props驼峰标识

props:{cInfo:{}_

<div id="app">

    <cpn:c-info="info"></cpn>

 

子传父:自定义事件

1.在子组件中,通过$emit()来触发事件;

2.在父组件中,通过v-on来监听子组件事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UFT-8">
    <tittle>Tittle</tittle>
</head>
<body>
<!--父组件模板-->
<div id="app">
    <cpn @itemclick="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
    <div>
        <button v-for="item in categories" @click="btnClick(item)">
            {{item.name}}
        </button>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
<!--子组件-->
    const cpn={
        template:'#cpn',
        data(){
            return{
                categories:[
                    {id:'aaa',name:'热门推荐'},
                    {id:'bbb',name:'手机数码'},
                    {id:'ccc',name:'家用家电'},
                    {id:'ddd',name:'电脑办公'}
                ]
            }
        },
        methods:{
            btnClick(item){
                this.$emit('itemclick',item)
            }
        }
    }
<!--父组件-->
    const app=new Vue({
        el:'#app',
        commponents:{
            cpn
        },
        methods:{
            cpnClick(){
                concole.log('cpnClick');
            }
        }
    })
</script>
</body>
</html>

父子组件通信案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
 
</head>
<body>
<div id="app">
    <cpn :number1="num1"
         :number2="num2"
         @num1change="num1change"
         @num2change="num2change"/>
</div>
<template id="cpn">
    <div>
    <h2>props:{{number1}}</h2>
    <h2>data:{{dnumber1}}</h2>
    <input type="text" :value="dnumber1" @input="number1Input">
    <h2>props:{{number2}}</h2>
    <h2>data:{{dnumber2}}</h2>
    <input type="text" :value="dnumber2" @input="num2Input">
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const app=new Vue({
        el:'#app',
        data:{
            num1:1,
            num2:0
        },
        methods:{
            num1change(value){
                this.num1=parseFloat(value)
            },
            num2change(value){
                this.num2=parseFloat(value)
            }
        },
        components:{
            cpn:{
                template:'#cpn',
                props:{
                    number1:Number,
                    number2:Number
                },
            data(){
                return{
                    dnumber1:this.number1,
                    dnumber2:this.number2
            }
            methods:{
                num1Input(event){
                    this.dnumber1=event.target.value;
                    <!--2.为了让父组件可以修改值,发出一个事件--> 
                    this.$emit('num1change',this.dnumber1)
                    <!--3.同时修师dnumber2的值-->             
                    this.dnumber2=this.dnumber1*100
                    this.$emit('num2change',this.dnumber2);
                }
                num2Input(event){
                    this.dnumber=event.target.value;
                    this.$emit('num2change',this.dnumber)
                    this.dnumber1=this.dnumber2/100;
                    this.$emit('num1change,this.dnumber1);
            }
        }
    }
}
})
</script>
</body>
</html>

父子组件的访问方式:(父访问子)

$children

new Vue({

  methods:{

    btnClick(){ 

      for(let c of this.$children){

        c.showMessage();

       }

this.$childre[3].name

ref

<cpn ref="aaa">

this.$refs.aaa.name

(子访问父)$parent

new Vue({

  components:{

    cpn:{

      template:'#cpn',

      components:{

        ccpn:{

          template:'#ccpn',

          methods:{
          btnClick(){
            console.log(this.$parent);
            console.log(this.$parent.name);
        
            console.log(this.$root);
            console.log(this.$root.message);
          }
          }
        }
    }
  }
})

slot 插槽

<cpn><span>哈哈哈</span></cpn>

<template id="cpn>

  <div>

    <slot>我是擦超的默认值</slot>

  </div>

</template>

1.插槽的基本使用<slot></slot>

2.插槽的默认值<slot>button</slot>

3.如果有多个值,同时放入到组件进行替换时,一起作为替换元素

编译作用域:

Stack Overflow/GitHub

在自己作用域查找变量

作用域插槽的使用

为了用一样的数据按不同形式呈现出来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
 
</head>
<body>
<div id="app">
    <cpn></cpn>
    <cpn>
        <!--目的是获取子组件中的planguages-->
        <template slot-scope="slot">
            <!--<span v-for="item in slot.data"><{{item}}</span>-->
            <span>{{slot.data.join('-')}}</span>
        </template>
    </span>
    <cpn>
        <template slot-scope="slot">
            <span>{{slot.data.join('#')}}</span>
        </template>
    </cpn>
</div>
<template id="cpn">
    <div>
        <slot :data="planguages">
        <ul>
            <li v-for="item in planguages">{{item}}</li>
        </ul>
    </div>
</template>
<script src="../js/vue.js"></script>
    const app=new Vue({
        el:'#app',
        components:{
            cpn:{
                template:'#cpn',
                data(){
                    return{
                        planguages;['JavaScript','C++','Java','Python','Go']
                    }
                }
            }
        }
    })
</script>
</body>
</html>

模块化开发

防止命名冲突>闭包:同时可以使用别的模块的东西

var ModulA=(function(){
    //1.定义一个对象
    var obj={}
    //2.在对象内部添加变量和方法
    obj.flag=true
    obj.myFunc=function(info){
        concole.log(info);
    }
    //3.将对象返回
    return obj
}){}

if(ModuleA.flag){
    console.log('小明是个天才');
}
ModuleA.myFunc('小明长得帅')
console.log(ModuleA);

CommonJS

导入和导出

//导出
module.exports={
    flag:true,
    test(a,b){
        return a+b
    },
    demo(a,b){
        return a*b
    }
}

//导入
let {test,demo,flag}=require('moduleA');
//等同于
let _mA=require('moduleA');
let test test=_mA.test;
let demo=_mA.demo;
let flag=_mA.flag;    

ES模块化的导入导出

//导出:
//在需要导出数据的js文件写
//方式1 
export{
    flag,sum
}
//方式2:
export var num1=1000;
export var height=1.88
//导出函数、类
export function mul(num1,num2){
    return num1*num2
}
exxport class Person{
    run(){
        console.log('在奔跑');
    }
}
//5.export Default,只能有1个,导入时自主命名
export default function(argument){
    console.log(argument);
}



//导入
//在需要导入数据的js文件写
//1.导入的{}中定义的变量
import {flag,sum} from "./aaa.js";
if{flag{
    console.log('小明');
    console.log(sum(20,30));
}

//2.直接导入export定义的变量
import {num1,height} from "./aaa.js";

console.log(num1);
console.log(height);

//3.导入export的function/class
import {mul,Person) from "../aaa.js";
console.log(mul(30,50));

const p=new Person();
p.run()

//4.导入默认
import addr from "./aaa.js";
addr('你好啊'));

//5.导入全部
import * as from ",/aaa.js";

//html文件
<script src="info.js" type="module"></script>
<script src="module.js" type="module"></script>

webpack

webpack安装

1.查看自己的node版本:node -v

2.全局安装webpack: npm install webpack@3.6.0 -g

3.局部安装webpack:npm install webpack@3.6.0 --save-dev

当在package.json定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack

在终端直接执行webpack命令,使用的全局安装的webpack

webpack的使用过程

使用webpack指令打包: webpack src/main.js dist/bundle.js

npm run build->webpack

//1.使用commonjs的模块化规范
const {add,mul}=require('./mathUtils.js')
console.log(add(20,30));

//2.使用ES6的模块化的规范
import {name,age,height} from "./info";
console.loge(name);

//3.依赖css文件
require('./css/normal.css')

打包后会在dist文件下,生成一个bundle.js文件,是webpack处理了项目直接文件依赖生成的一个js文件,我们只需要将这个js文件在index.html中引入即可

<script src="./dist/bundle.js"></script>

webpack.config.js配置和package.js

可以将入口出口这讲个参数写在配置中,在运行时直接读取,这样就不用每次使用webpack的命令都需要协商入口和出口了:

const path=require('path')
module.exports={
    entry:'./drc/main.js',
    output:{
        path:path.path.resolve(__dirname,'dist'),//注意:path通常是一个绝对路径
        filename:'bundle.js'
    }
}

webpack中使用css文件的配置

www.webpackjs.com

loader:可以不仅仅webpack打包js文件

1.通过npm安装需要使用的loader

npm install --save-dev css-loader

npm install --save-dev style-loader

2.在webpack.config.js中的modules关键字下进行配置

//webpack.config.js下
module.exports={
    module:{
        rules:[
            {
                test:/\.css$/,//正则表达式
//css-loder只是负责j加载,style-loader负责将样式添加到DOM中,从右向左
                use:['style-loader','css-loader']
            }
        ]
    }
}

less文件的处理

依赖less文件,main.js

require('./css/special.less')

www.webpackjs.com,点击less-loder的安装

图片文件的处理

安装url-loader

涉及图片的,都加上:/dist/:配置:   publicpath:'dist/'

大于配置limit,需要安装file-loader

文件命名:use: name:'img/[name].[hash:8].[ext]'

ES6转ES5的label

1.npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

2.配置webpack.config.js文件

{
    test:/\.m?js$/,
    exclude:/{node_modules|boewr_components}/,
    use:{
        loader:'babel-loader',
        options:{
            presets:['es2015']
        }
    }
}

3.重新打包。查看bundle.js文件,发现其中的内容变成了ES5的语法

使用VUE的配置过程

1.安装vue

npm install vue--save (安装运行都需要依赖vue,而不是仅仅开发,不接-dev)

2.在main.js中直接使用

import Vue from 'vue'

const app=new Vue({
    el:'#app',

    data:{

        message:'Hello Webpack'

    }

在在html挂在vue

3.两版本:runtime-only:不支持template  ;  runtime-compiler: 支持template

指定版本:

modules.eaports={
    resolve:{
        alias:{
            'vue$':'vue/dist/vue.esm.js'
        }
    }
}

创建vue时template和el的关系

Vue使用的终极版本

npm install vue-loder vue-template-compiler --save-dev

vue-loader加载文件,vue-template-compiler编译文件

想只用vue-loder还需要配置,或者找低于13版本,去package.json修改

<template>
    <div>
        <h2>我是cpn组件的标题</h2>
        <p>我是cpn组件的内容</p>
        <h2>{{name}}</h2>
        </Cpn>
    </div>
</template>
<script>
    import Cpn from './Cpn'
    export default{
        name:"Cpn",
        components:{
            Cpn
        },
        data(){
            return{
                name:'CPN组件的name'
            }
    }
</script>

如果想要省略后缀名,可以在配置文件webpack.config.js中添加:
module.exports={
  resolve:{

    extensions:['.js','.css','.vue']\  }

}

横幅plugin的使用

plugin是插件,对webpack现有功能的各种扩展,比如打包优化,文件压缩等

plugin是对webpack本身的扩展,是一个扩展器;

loader主要用于转换某些类型的模块,它是一个转换器

plaugin的使用过程:

1.通过npm安装需要使用的plugins

2.在webpack.config.js中的plugins中配置插件

 

添加版权的plugin: BannerPlugin

1.在webpack.config.js文件中添加:

const webpack=require('webpack')

module.exports={

  plugins:[

    new webpack.BannerPlugin('最终版权归aaa所有')

  ]

}

2.重新打包

HtmlWebpackPlugin的使用

HtmlWebpackPlugin作用:

1.自动生成一个index.html文件(可以指定模板来生成)

2.将打包的js文件,自动通过script标签插入到body中

安装插件:

1.npm install html-webpack-plugin --save-dev

2.在配置文件webpack.config.js中:

const HtmlWebpackPlugin=require('html-webpack-plugin')

module:export={

  plugins:[

    new webpack.BannerPlugin('最终版权归aaa所有'),

    new HtmlWebpackPlugin({

      template:'index.html'

   })

  ]

}

3.npm run build

更换目录:cd 05-webpack

UglifyjsWebpackPlugin

UdlifyWebpackPlugin:丑化,对打包的js文件压缩

1.npm install uglifyjs-webpack-plugin@1.1.1 --save-dev

2.修改webpack.config.js文件,使用插件

const UglifyjsWebpackPlugin=require('uglifyjs-webpack-plugin')

Plugins:[new UglifyjsWebpackPlugin()]}

3.npm run build

webpack-dev-server服务器搭建

避免多次打包,修改时可自动刷新,等完成之后在npm run build

1.npm install webpack-dev-seaver@2.9.3--save-dev

2.配置

modules=export{
  devServer:{
    contentBase:'.dist',

    inline:ture //实时刷新

  }

3. 配置是局部,要使用全局

方法1../node_modules/.bin/wenpack/webpack-dev-server //写相对路径,找到文件夹

方法2.去package.json文件,{"scripts:{"dev":"webpack-dev-server --open"}}//优先到本地找

4. npm run dev

配置文件的抽离

开发和发布依赖不同的配置文件

1.安装 npm install webpack-merge//对两个文件进行合并

2.base.config.js

module.exports={
    entry:'./src/main.js',
    output:{
        path:path.resolve(__dirname,'../dist')
    }

prop.config.js编译文件

const UglifyWebpackPlugin=require('uglifyjs-webpack-plugin')
const webpackMerge=require('webpack -merge')
const baseConfig=require('./base.config')

module.export=webpackMerge(baseConfig,{
    plugins:[
        new UglifyjsWebpackPlugin()
    ]
})

dev.confug.js开发文件

3.package.json

{"scripts":{"build":"webpack --config ./build/prod.config.js","dev":"webpack-dev-server --open --config ./build/dev.config.js"}}

4.npm run build

5.npm run dev

Vue CLI脚手架的介绍和安装

CLI Command-Line Interface

使用vue-cli可以快速搭建Vue开发环境以及对应的webpack配置

Vue CLI使用前提-Node,webpack

1.安装NodeJS

网址:http://nodes.cn/download/

2.检查安装的版本

xmg$ node -v

xmg$ npm -v

npm: Node Package Manager

3.webpack的全局安装

npm install webpack -g

Vue CLI的使用

https://cli.vue.js.org/zh/guide/installation.html

1.安装vue脚手架

npm install -g @vue/cli

2.拉取2.x模板

npm install @vue/cli-init -g

3.VueCLI2初始化项目: vue init webpack my-project

Vue CLI3初始化项目:vue create my-project

CLI2初始化项目的过程

vue init webpack my-project

?propject name my-project

?project description test vue cli2

?Author coderwhy

>Runtimer+Compiler Runtime-only

?install vue-rounter 安装路由

?use eslint to lint your code 使编码规范

?pick an eclint preset Standard 

?Set up unit tests 单元测试 n

?Setup e2e tets tests with Nightwatch 端到端自动测试 n

Yes,use NPM

CLI2的目录结构解析

v8引擎,直接把js编译成二进制代码,跳过字节码

安装CLI错误和ESlint规范

错误:去到目录删除ache

eslint: 编写代码规范

关掉eslint :config->index.js->useesline:false

runtime-compiler和runtime-only

template->ast->render->vdom->真实DOM

区别只在main.js

const cpn={
    template:'<div>{{message}}</div>',
    data(){
        return{
            message:'我是组件message’
        }
    }
}

new Vue({
    el:'#app',
    render:function(createElement){
        //1.普通用法:createElement('标签',{标签的属性},['])
        return createElement('h2',
            {class:'box'},
            ['Hello World', createElement('button',['按钮'])])
        //2.传入组件对象
        return createElement(cpn)

    }
})

//由vue-template-compiler解析成render函数(开发时依赖)

VueCLI3创建项目和目录结构

rc-run command

Vue CLI3 配置文件修改,查看

运行时编译runtime-compiler

配置隐藏

vue ui 图形化界面操作

git 本地仓库

创建vue.config.js修改配置

箭头函数的基本使用和this指向

<script>
//1.放入两个参数
const sum=(num1,num2)=>
    return num1+num2
}
//2.放入一个参数
const power=num=>{
    return num*num
}
//3.函数中打印,如果按4写,打印undefined,console.log返回值
const test=()=>{
    console.log('Hello');
}
//4.函数只有一行
const mul=(num1,num2)=>num1*num2

//箭头函数中的this是向外层作用域一层层查找
const obj={
    aaa(){
        setTimeout(funxtion(){
            setTimeout(function(){
                console.log(this);//window
            setTimeout(()=>{
                console.log(this);//window对象
            })
        })
        setTimeout(function(){
            console.log(this);//window
        setTimeout(()=>{
            console.log(this);//obj对象
        })
    }
}            

什么是路由和其中的映射关系

映射表:【内网ip1:电脑mac标识1】

前端渲染后端渲染和前端路由后端路由

url的hash和HTML5的history

改变url没刷新

1.URL的hash也就是锚点,本质是该百年windoe.location的href属性,改变url页面没刷新

hash :location.hasn='aaa'  ->  localhost:8081/#/aaa

2.栈结构

history.pushState({},'','home')

history.back() :移除最新一个,显示倒数第二个,出栈

history.replaceState({},'','home') :替换,没有返回

history.pushState({},'','home')  -> history.upshState({},'','abort') ->history.go(-1)跳到倒数第二个 、history.forward(-1)

vue-router 安装和配置

1..安装: npm install vue-router --save

2.在模块化工程中使用(通过Vue.use())

导入路由对象,并且调用Vue.use(VueRouter)

创建路由实例,并且传入路由映射配置

在Vue实例中挂在创建的路由实例

//index.js
//配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'

//1.通过Vue.use(插件),安装插件
Vue.sue(VueRouter)

//2.创建VuerRouter对象
const routes=[]

const router=new VueRouter({
    routes
})

//3.将router对象传入到Vue实例
export default router

//main.js
import Vue from 'vue'
import App from './App'
import couter from'./router'

Vue.config.productionTip=false

new Vue({
    el:'#app',
    router,
    render:h=>h(App)
})

路由映射配置和呈现

3.使用

创建路由组件

配置路由映射:组件和路径映射关系

使用路由:通过<router-link>和<router-view>

//index.js
//配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from'../components/Home'
import About from '../comopnents/About'

//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter)

//2.创建VuerRouter对象
const routes=[
    {
        path:'/home',
        component:Home
    },
    {
        path:'/about',
        component:About
    }
]

const router=new VueRouter({
    routes
})

//3.将router对象传入到Vue实例
export default router
// App.vue
<template>
    <div id="app">
        <router-link to="/home">首页</router-link>
        <router-link to="/about">关于</router-link>
        <router-view></router-view>
    </div>
</template>

<script>
export default{
    name:'App'
}
</script>

<style>

</style>

路由默认值和修改为history模式

const routes=[
    {
//默认值
        path:'/',
        redirect:'/home'
    },

    {
        path:'/home',
        component:Home
    },
    {
        path:'/about',
        component:About
    }
]

//改为history
const router=new VueRouter({
    //配置路由和组件之间的应用关系
    routes,
    mode:'history'
})

router-link其他属性

<router-link to="/home" tag="button" replace active-class="active">首页</router-link>


const router=new VueRouter({
    //配置路由和组件之间的应用关系
    routes,
    mode:'history',
    linkactiveClass:'active'
})
//或者在路由修改属性名,再修改样式

通过代码跳转路由

//App.vue
<template>
    <div id="app">
        <button @click="homeClick">首页</button>
        <button @click="aboutClick">关于</button>
    </div>
</template>

<script>
export default{
    name:'App'
    method:{
        homeClick(){
            //this.$router.replace('/home')
            this.$router.push('/home')
        }
        aboutClick(){
            //this.$router.replace('/about')
            this.$router.push('/about')
        }
    }
}
</script>

vue-router 动态路由的使用

//app.vue

<router-link v-bind:to="'/user/'+useId">用户</router-link>

<script>
export default{
    name:'App',
    data(){
        return{
            useId:"list'
        }
    }
}

//User.vue
<template>
    <div>
        <h2>我是用户界面</h2>
        <h2>{{userId}}</h2>
        <h2>{{$.route.params.abc}}</h2>
    </div>
</template>

<script>
    export default{
        name:"User",
        computed:{
            userId(){
//活跃的路由
                this.$route.params.abc
}


//index.js
const routes=[
    {
        path:'/user/:abc',
        component:User

]
    

打包文件的解析

路由懒加载的使用

把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件

路由懒加载主要的作用是将路由对应的组件打包成一个个的js代码块,只有在这个路由被访问到的时候,才加载对应的组件

//index.js
const Home=()=>import('../components/Home')
const Abour=()=>import('../components/About')
const User=()=>import('../components/User')

Vue.use(VueRouter)

const routes=[{
    path:'',
    redirect:'/home'
},
    {
    path:'/home',
    component:Home
}]

//方式1:(不常用) 结合Vue的异步组件和Webpack的代码分析
const Home=resolve=>{require.ensure(['../components/Home.vue'],()=>{resolve(require('../components/Home.vue'))})};

//方式二:AMD写法
const About=resolve=>require(['../components/About.vue'],resolve);

//方式三:在ES6中,我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割
const Home=()=>import('../components/Home.vue')

嵌套路由

 一个路径映射一个组件,访问者两个路径也会分别渲染这两个组件

实现嵌套路由有两个步骤:

1.创建对应的子组件,并且在路由映射中配置对应的子路由

2.在组件内部使用<router-view>标签

//index.js
const HomeNews=()=>import('../components/HomeNews')
const HomeMessage=()=>import('../components/HomeMessage')

const routes=[
    {
        path:'/home',
        component:Home,
        children:[{    
            path:'news'
            component:HomeNews
    },
    {
        path:'message',
        component:HomeMessage   
     }
    {
        path:'',
        component:HomeMessage   
     }
]
        }

]

//Home.vue
<template>
<div>
    <router-link to="/home/news">新闻</router-link>
    <router-link to="/home/message">消息</router-link>
    <router-view></router-view>
</div>
</template>

vue-router参数传递

方式:params和query

URL: 协议://主机:端口/路径?查询

scheme://host:port/path?query#fragment

<router-link :to="{path:'/profile',query:{name:'why',age:18,height:1.88}}">
<template>
    <h2>{{$route.query}}</h2>
</template>
//通过按钮点击,跳转
<button @click="userClick">用户</button>
<button @click="profileClick">档案</button>

export default{
    methods:{
        userClick(){
            this.$router.push('/user/'+this.userId"
        },
        profileClick(){
            path:'/profile',
            query:{
                name:'kobe',
                age:19,
                height:1.87
            }
        }       
    }
}
    

vue-router和route的由来

router: new router

route:活跃的路由

vue router全局导航守卫

生命周期:创建created(){},挂载mounted(){},更新updated(){}

更换标题

export default{
    name:"Home",
    data(){
        created(){
            document.tittle='首页'
        }
    }
}

全局

//index.js
const routes=[{path:'/home',component:Home,meta:{tittle:'首页'}},{{path:'/user',component:User,meta:{tittle:'用户'}}]

const router=new VueRouter({
    //配置路由和组件之间的应用关系
    routes,
    mode:'history',
    linkActiveClass:'active'
})

router.beforeEach(to,from,next)=>{
    //从from跳转到to
    document.tittle=to.meta.tittle
        
    //有路由嵌套的时候
    document.tittle=to.matched[0].meta.tittle
    next()
})
//前置守卫
router.beforeEach(to,from,next)=>{
    //从from跳转到to
    document.tittle=to.meta.tittle
        
    //有路由嵌套的时候
    document.tittle=to.matched[0].meta.tittle
    next()
})

//后置钩子(hook)
router.afterEach(to,from)=>{
    console.log('---')
})

vue-router-keep-alive

被缓存,重新点击时不会重新创建除了Profile和User

<keep-alive exclude="Profile,User">

  <router-view/>

</keep-alive>

 

//记录上一次离开的path,嵌套路由

activated(){

  this.$router.push(this.path);

beforeRouteLeave(to,from,next){

  this.path=rhis.$route.path;

  next()

}

tabBar-基本结构的搭建

1.vue init webpack tabbar

案例

//App.vue
<template>
    <div id="app">
        <router-view></router-view>
        <main-tab-bar/>
    </div>
</template>

<script>
    import MainTabBar from './components/MainTabBar'
    export default{
        name:'App',
        components:{
            MainTabBar
        }
    }
</script>

<style>
    @import ".asset/css/base.css";
</style>
//src/assets/css/base.css
body{
    padding:0;
    marginL0;
}
//src/assets/main.js

import Vue from 'vue'
import App from ',/App'

Vue.config.produtionTip=false

new Vue({
    el:'#app',
    render:h=>h(App)
        
})

//在components新增文件夹tabbar,TabBar.vue
<template>
    <div id="APP">
        <slot></slot>
    </div>
</template>

<script>
    export default{
        name:"TabBar"
    }
</script>

<style scope>
    #tab-bar{
        display:flex;
        background:#f6f6f6;

        position:fixed;
        left:0;
        right:0;
        bottom:0;

        box-shadow:0px -1px 1px rbga(100,100,100,.01);
        
    }
    
    .tab-bar-item{
        flex:1;
        text-align:center;
        height:49px;
    }

    .tab-bar-item img{
        width:24px;
        height:24px;
    }
</style>
//conponent/tabbar/TabBarItem.vue
<template>
        <div class="tab-bar-item" @click="itemClick>
            <div v-if="!isActive"><slot name="item-icon"></slot><.div>
            <div v-else><slot name="item-icon-active"></slot></div>
            <div :style="activeStyle"><slot name="item-icon-active"></slot></div>           
        </div>
</template>

<script>
    export default{
        name:"TabBarItem",
        props:{
            path:String,
            activeColor:{
                type:String,
                default:'red'
            }
        },
       computed:{
            isActive(){
                return this.$route.path.indexOf(this.path)!=-1
            },
            activeStyle(){
                return this.isActive ? {color:this.activeColor}:{}
            }
        }
        methods:{
            itemClick(){
                this.$router.push(this.path)
    }
</script>



<style scoped>
    .tab-bar-item{
        flex:1;
        text-align:center;
        height:49px;
        font-size:14px;
    }

    .tab-bar-item img{
        width:24px;
        height:24px;
        margin-top:3px;
        vertical-align:middle;
        margin-bottom:2px;
    }

</style>
//cd 03-tabbar
//npm install vue-router --save运行时依赖
//src/routerindex.js

import vue from vue
import VueRouter from 'vue-router'

const Home=()=>import('../views/home/Home')
const Home=()=>import('../views/category/Category')
const Home=()=>import('../views/cart/Cart')
const Home=()=>import('../views/profile/Profile')

//1.安装插件
Vue.use(VueRouter)

//2.创建路由
const router=[
    {
        path:'',
        redirect:'/home'
    },
    {
        path:'/home',
        component:'/Home'
    },
    {
        path:'/category',
        component:'/Category'
    },
    {
        path:'/cart',
        component:'/Cart'
    },
    {
        path:'/profile',
        component:'Profile'
    }
]

const router=new VueRouter({
    router
    mode:'history'

})

//3.导出router
export default router
//src/vies/home/Home.vue|Category.vue|Profile.vue|cart.vue
<template>
    <h2>分类</h2>
</template>

<script>
    export default{
        name:"Category"
    }
</script>

<styly scoped>
</style>
    
//src/components/tabbar/MainTabBar.vue
//注意路径
<template>
    <tab-bar>
        <tab-bar-item path="/home" activeColor="pink">
            <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
            <img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
            <div slot="item-text">首页</div>
        </tab-bar-item>
        <tab-bar-item path="/category" activeColor="pink">
            <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
            <img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
            <div slot="item-text">分类</div>
        </tab-bar-item>
        <tab-bar-item path="/cart" activeColor="pink">
            <img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
            <img slot="item-icon-active" src="./assets/img/tabbar/shopcart_active.svg" alt="">
            <div slot="item-text">购物车</div>
        </tab-bar-item>
        <tab-bar-item path="/profile" activeColor="pink">
            <img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
            <img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
            <div slot="item-text">我的</div>    
        </tab-bar-item>
    </tab-bar>
</template>

<script>
    import TabBar from './components/tabbar/TabBar
    import TabBarItem from './components/tabber/TabBarItem'
    export default{
        name:"MainTabBar",
        components:[
            TabBar,
            TabBarItem
    }
</script>

<style scoped>
</style>
//起别名
//webpack.base.conf.js

resolve:[
    alias:{
        '@':resolve('src'),
        'assets':resolve(src/assets')
    }
},

//MainTabBar.vue
<img slot="item-icon" src="~assets/img/tabbar/home.svg" alt="">
import TabBar from '@/components/tabbar/TabBar'

Promise

promise的介绍和基本使用

promise是异步编程的解决方案。

sync同步async异步

//链式编程
<script>
    new Promise((resolve,reject)=>{
//第一次请求
        setTimeout(()=>{
            resolve()
        },1000)
        }).then(()=>{
//第一次处理
            console.log('Hello World');
//第二次请求
            return new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    resolve()
                },1000)
            })
        }).then(()=>{
//第二次处理
                concole.log('Hello Vuejs');
                return new Promise((resolve,reject)=>{
//第三次请求
                    setTimeout(()=>{
                        resolve()
                    },1000)
                    })
                })
            }).then(()=>{
//第三次处理
                        console.log('Hello Python');
                    })

propmise的三种状态

pending等待,fulfill满足(调用resolve时->then()),reject拒绝(调用reject时->catch())

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<script>
    new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject('error message')},1000)
    }).then(data=>{
        console.log(data);
    },err=>{
        console.log(err)
    })
</script>
</body>
</html>

promise链式调用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<script>
    new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('aaa')
        },1000)
    }).then(res=>{
//自己处理10行代码
        console.log(res,'第一层的10行处理代码');
//对结果进行第一次处理
//简写:return Promise.resolve(res+'111')
//更简写: return res+'111'
//reject: return Promise.reject(;wrror message') 或者 throw 'error message'
        return new Promise((resolve)=>{
            resolve(res+'111')
        })
    }).then(res=>{
        console.log(res,'第二层的10行处理代码');
        return new Promise(resolve=>{
            resolve(res+'222')
        })
    }).then(res=>{
        console.log(res,'第三层的10行处理代码');
    })
//如果是reject 后面还需要接:.catch(err=>{console.log(err);})
</script>
</body>

promise的all方法

拿到两个才执行

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle<tittle>
</head>
<body>
<script>
Promise.all([
    new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('result1')
        },2000)
    }),
    new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('result2')
        },1000)
    })
]).then(results=>{
    console.log(results);    
})
</script>
</body>
</html>

Vuex

vuex概念和作用解析

Vuex是一个专门为Vue.js应用程序开发的状态管理模式,就是把需要多个组件共享的变量全部存储在一个对象里面。响应式

token,头像,位置,收藏,购物车

Talk is cheap,Show me the code.

单页面到多页面状态管理切换

单页面:state,action,view

插件:devtools

我们来对使用步骤,做一个简单的小节: 
  01.提取出一个公共的store对象,用于保存在多个组件中共享
的状态
  02.将store对象放置在new Vue对象中,这样可以保证在所有
的组件中都可以使用到
  03.在其他组件中使用store对象中保存的状态即可
➢通过this.$store.state.属性的方式来访问状态
➢通过this.sstore.commit('mutation中方法)来修改状态
注意事项:
  我们通过提交mutation的方式,而非直接改变
store.state.count.
  这是因为Vuex可以更明确的追踪状态的变化,所以不要直接
改变store.state.count的值。

//index.js
import Vue from 'vue'
import Vuex from 'vues'

//1.安装插件
Vue.use(Vuex)

//2.创建对象
const moduleA={
    state:{
        name:'zhangsan'
    ",
    mutation:{
        updateName(state,payload){
            state.name=payload
        }
    },
    getters:{
        fullname(state){
            return state.name+'111'
        },
        fullname2(state,getters){
            return getters.fullname+'222'
        },
        fullname3(state,getters,rootState){
            return getters.fullname2+rootState.counter
        }
    }
    action:{
        aUpdateName(context){
            setTimeout(()=>{
                context.commit('updateName','wantwu')
            },1000)
        }
    }
}

const store =new Vuex.Store({
    state:{
        counter:1000
        student:[
            {name:'why',age:18},
            {name:'kobe',age:21},
            {name:'james',age:15},
            {name:'curry',age:39}
        ]
    },
//参数修改只能在mutations中
    mutations:{
        //方法
        increment(state){
            state.count++
        },
        decrement(state){
            state.count--
        },
        incrementCount(state,count){
            state.counter+=count
        },
//风格1
//        addStudent(state,stu){
//            state.student.push(stu)
        }
//风格2
        addStudent(state,payload){
            state.student.push(payload.stu)
        },
        updateInfo(){
            this.$store.commit('updateInfo')
    },
    getters:{
        powerCouter(state){
            return state.counter*state.counter
        },
        more20stu(state){
            return state.students.filter(s=>s.age<20)
        },
        more20stuLength(state,getters){
            return getters.more20stu.length
        },
        moreAgestu(state){
            return function(age){
                return state.students.filter(s=>s.age>age)
            }
        }
    },
    action:{
        aUpdateInfo(context,payload){
            return new Promise(resolve,reject)=>
                setTimeout(()=>{
                    context.commit('uodateInfo');
                    console.log(payload);
                    resolve('111')
                },1000)
            })
        }
    },

    modules:{
        a:moduleA
    }
})

//3.导出store独享
export default store
//main.js

import Vue from 'vue'
import App from'./App'
import store from './store'

Vue.config.productionTip=false

new Vue({
    el:'#app',
    store,
    render:h=>(App)
})
//App.vue
<template>
    <div id="app">
        <h2>{{$store.state.counter}}</h2>
        <h2>{{counter}}</h2>
        <button @click="addition">+</button>
        <button @click="$substration">-</button>
        <button @click="addCount(5)"></button>
        <button @click="assStudent"></button>
        <button @click=" updateInfo"></button>
        <button @click=" updateName"></button>
        <button @click="asyncUpdateName">异步修改名字</button>

        <h2>---App内容:getters相关信息---</h2>
        <h2>{{$store.getters.powerCounter}}</H2>
        <h2>{{$store.getter.more20stu}}</h2>
        <h2>{{$store.getters.more20stuLength}}<h2>
        <h2>{{$store.getters.moreAgeStu(12)}}</h2>
        <h2>{{$store.getters.fullname}}</h2>
        <h2>{{$store.getters.fullname2}}</h2>
        <h2>{{$store.getters.fullname3}}</h2>
        
        <hello-vueex/>
    </div>
</template>

<script>
    import HelloVuex from './components/HelloVuex'

    export default{
        name:'App',
        components:{
            HelloVuex
        },
        data(){
            return{
                message:'我是App组件'
            }
        },
        methods:{
            addition(){this.$store.commit('increment'},
            subtraction(){this.$store.commit('decrement')}
            addCount(count){
//payload:负载
//1.普通的提交封装
//                this.$store.commit('incrementCount',count)
//2.另一种风格:
                  this.$store.commit({
                    type:'incrementCount',
                    count
})
            },
            addStudent(){
                const stu={id:114,name:'alan',age:35}
                this.$store.commit('addStudent',stu)
            },
            updateInfo(state){
                state.info.name='coderwhy'
                Vue.set(state.info,'address','洛杉矶')
                Vue.delete(state.info,'age')
            },
            updateInfo(){
                this.$store.dispatch('aUpadteInfo','我是携带的信息')
                .then(res=>{
                    console.log('里面完成了提交');
                    console.log(res);
                })
            },
            updateName(){
                this.$.store.commit('updateName','lisi')
            },
            asyncUpdateName(){
                this.$store.dispatch('aUpdateName')

            }
        }
        
    }
</script>
//HelloVuex.vue
<template>
    <div>
        <h2>{{$store.state.counter}}</h2>
    </div>
</template>

<script>
    export default{
        name:"HelloVuex"
    }
</script>

<style scoped>

</style>

state单一状态树的概念

Single Source of Truth

统一放在store里面

响应规则

命名

网络模块的封装

安装axios :nom install axios --save

//main.js
import Vue from 'vue'
import App from './App'

//main.js
import axios from 'axios'

Vue.config.productionTip=false

new Vue({
    el:'#app',
    render:h=>h)App)
})
//1.
axios({
//    url:'httpbin.org'
    url:'http://123.207.32.32:8000/home/data',
    params:{
        type:'pop',
        page:'1'
    }
}).then(res->{
    console.log(res);

})
//2.axios发送请求
axios.all([axioc({
    url:'http://123.207.32.32:8000/home/multidata'
}),axios({
    url:'http://123.207.32.32:8000/home/data',
    params:{
        type:'sell',
        page:5
    }
})]).then(result=>{  //then(axios.spread((res1,res2)=>
    console.log(results);
    console.log(results[0];
    console.log(result[1];
})
//3.全局
//axios.default.baseURL=''
//axios.default.timeout=5000
//4.创建对应的axios实例
const instance1=axios.create({
    baseURL:'http://123.207.32.32:8000',
    timeout:5000
})
const instance2=axios.create({
    baseURL:'http://222,111,33,33:8000',
    timeout:1000,
    header:{}
})
instance({
    url:'/home/muitidata'
}).then(res=>{
    console.log(res);
})
instance2({
    url:'/home/data',
    params:{
        type:'pop',
        page:1
    }
}).then(res=>{
    console.log(res);
})


//5.封装
request({
    url:'/home/multidata'
}).then(res=>{
    console.log(res);
}).catch(err->{
    console.log(err);
})

 

封装

networks->requires.js

//request.js
import axios from 'axios'

export function request(config){
    //1.创建axios 的实例
    const insstance = axios.create({
        baseURL:'http://123.207.32.32:8000',
        timeout:5000
    })
    //2.axios的拦截器
    instance.intercaptors.request.use(consig=>{
        console.log(config);
        //1.比如config中的一些信息不符合服务器的要求
        //2.比如每次发送网络请求时,都希望在界面中显示一个请求的图标
        //3.某些网路请求(比如登录(token)),必须携带一些特殊信息
        return config
    },err=>{
        console.log(err);
    })

    //2.2响应拦截
    instance.intercaptors.response.use(res=>{
        return res.data
    },err=>{
        console(err);
    })
    //3.发送真正的网络请求
    return instance(config)
}

项目创建和GITHUB托管

1.vue create supermall

2.npm run serve

3.托管

4.git clone https://github.com//coderwhy/supermall.git

5.cd supermall -> git status ->git add. ->git commit -m'初始化项目‘ ->git push

(或者 cd.. ->cd suoermall ->git remote add origin https://github.com.coderwhy/test.git ->git push -u origin master)

1.划分目录结构

2.css引用

nomalize.css

base.css @import ".nomalize.js

App.vue <style>@import "./assets/css/base.css";</style>

3.vue.config和editconfig

新建vue.config.js

module.exports={
    configureWebpack:{
        resolve:{
            alias:{
                'assets':'@/assets',
                'common':'@/common',
                'components':'@/components',
                'network':'@network',
                'views':'@/viess',
            }
        }
    }
}

editorconfig

root=true

[*]
charset=utf-8
indent_style=space
indent_size=2
end_of_line=1f
insert_final_newline=true
trim_trailing_whitespace=true

//编写规范

4.tabbar引入和项目模块划分

0846

npm install vue-router --save

//router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

const Home=()=>import('../views/home/Home')

//1.安装插件
Vue.use(VueRouter)

//2.创建router
const routes=[
//映射关系
    {path:'',redirext:'/home'}{}{}
]
const router=new VueRouter({
    routers,
    mode:'history'

})

export default router
//views/main.js
import Vue from 'vue'
import App from'./App.vue'
import router from './router'

Vue.config.productionTip=false
//默认$bus 没有值
Vue.proptotype.$bus=new Vue()

new Vue({
    render:h=>h(App),
    router
}).$mount('#app')

5.小图标的修改和路径问题

小图标:public/favison.ico

//index.html

<html lang="en">
    <head>
        <link rel="icon" href="<%=BASE_URL %>favicon.ico">
//base url 是动态获取路径

6.首页导航栏的封装和使用

//src/views/home/Home.vue
<template>
    <div id="home">
        <nav-bar class="home-nav"><div slot="center">购物街</nav-bar>
         <tab-control :tittles="['流行,'精选']" @tabClick="tabClick" ref="tabControl1" class="tab-control" v-show="isTabFixedd"/>
        <scroll class='content' ref="scroll" :probe-type="3" @scroll="contentScoll" :pull-up-load="true" @pullingUp="load-more">
         <home-swiper:banners="banners"/>
        <recommend-view :recommends="recommend"/>
        <feature-view/>
        <tab-control :tittles="['流行,'精选']" @tabClick="tabClick" ref="tabControl2"/>
        <good-list :goods="showGoods()"/>
        </scroll>
        <back-top @click.native="backClick" v-show="isShowBackTop"/ >
    </div>
</template>

<script>
    import HomeSwiper from './childComps/HomeSwiper'
    impoer RecommendView from './childComps/Recommendview'
    import FeatureView from './childCopms/FeatureView'

    import NavBar from 'components/common/navbar/NavBar'
    import TabControl from 'components/content/tabControl/TabControl'
    import GoodList from './childComps/FeatureView'
    import BackTop from 'components/content/backTop/BackTop'

    import {getHomeMultidata}from "newwork.home"

    export default{
        name:"Home",
        components:{
            NarBar,    
            HomeSwiper,
            RecommendView,
            FeatureView,
            TabControl,
            BackTop
        },
    data(){
        return{
            banners:[],
            recommends:[],
            goods:{
                'pop':{page:0,list:[]},
                'new':{page:0,list:[]},
                'sell':{page:0,list:[]},
        },
        currentType:'pop',
        isShowBackTop:true
    },
    computed:{
        showGoods(){
            return this.goods[currentType].list
        }
    }
//网页加载前
    created(){
        //1.请求多个数据
       this.getHomeMultidata()

        //2.请求商品数据
        this.getHomeGoods('pop')
        this.getHomeGoods('new')
        this.getHomeGoods('sell')

        
        },
//网页加载后
    mounted(){
        const refresh=this.debounce(this.$refs.scroll.refresh,500)
        //3.监听发item中图片加载完成
        this.$bus.$on('itemImageLoad',()=>{
            refresh()
        })
    },
    methods:{
//监听方法 settimeout 会延迟到事件尾部执行,下一次循环执行
        debounce(func,delay){
            let timer=null
            return function(...args){
                if(timer) clearTimeout(()=>{
                    timer=setTimeout(()=>{
                        func.apply(this,args)
                    },delay)
                }
        },
        tabClick(index){
            switch(index){
                case 0:
                    this.currentType='pop'
                    break
                case 1:
                    this.currentType='new'
                    break
                case 2:
                    this.currentType='sell'
                    break
            }
            this.$refs.tabControl1.currentIndex=index;
            this.$refs.tabControl2.currentIndex=index;
        },
        backClick(){
            this.$refs.scroll.scrollTo(0,0)
        },
        contentScroll(position){
//判断BackTop是否显示
            this.isShowBackTop=(-position.y)>1000
//决定tabControl是否吸顶(position:fixed)
            this.isTabFixed=(-position.y)>this.tabOffsetTop
        },
        loadMore(){
            this.gethomeGoods(this.currentType)
//重新计算
            this.$refs.scroll.scroll.refresh()
        },
        SwiperImageLoad(){
            this.tabOffsetTop=this.$refs.tabControl2.$el.offsetTop;


//网络请求方法
        getHomeMultidata(){
            getHomeMultidata().then(res->{
                this.banners=res.data.banner.list;
                this.recommends=res.data.recommend.list;
            })
        },
        getHomeGoods(type){
            const page=this.goods[type].page+1
            getHomeGoods(type,page).then(res=>{
                this.goods[type].list.push(...res.data.list)
                this.goods[type].page+=1
//完成上拉加载更多
                this.$refs.scroll.finishPullUp()
            })
        }
    }
</script>

<style scope>
    #home{
        height:100vh;
        position:relative;
    }
    .home-nav{
        background-color:var(--color-tint);
        coloe:#fff;
        position:fixed;
        left:0;
        right:0;
        top:0;
        z-index:9;
    }
    .tab-control{
        position:sticky;
        top:44px;
    }
    .content{
        overflow:hiddem;
        position:absolute;
        top:44px;
        bottom:49px;
        left:0
        right:0
    }

    }
</style>
//src/component/common/tabbar/TabBar.vue
<template>
    <div id="nav-bar">
    <div class="left"><slot name="right"></slot></div>
    <div class="center"><slot name="center"></slot></div>
    <div class="right"><slot name="right"></slot></div>
    </div>
</template>
<script>
    export default{
        name:"NavBar"
    }
</script>
<style scoped>
    .nav-bar{
        display:flex;
        height:44px;
        line-height:44px;
        text-align:center;
    }
    .left,.right{
        eidth:60px;
    }
    .center{
        flex:1;
    }
</style>
//network/home.js
import {request} from ",/request";

export function getHomeMultidata(){
    return request({
        url:'/home/multidata'
    })
}

export function getHomeGoods(type,page){
    return request({
        url:'/home/data',
        params:{
            type,
            page
        }
    })
)

7.首页请求多个数据

network/request.js

axios

8.轮播图的展示

//components/common/swiper/SwiperItem.vue
<template>
    <div id="hy-swiper">
        <div class="swiper @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
            <slot></slot>
        </div>
        <slot name="indicator">
        </slot>
        <div class="indicator">
            <slot name="indicator" v-if="showIndicator && slideCount>1">
                <div v-for="(item,index) in slideCount" class="indi-item" :class="{active:index===currentIndex-1}" :key="index"></div>
            </slot>
        </div>
    </div>
</template>

<script>
    export default{
        name:"Swiper",
        props:{
            interval:{
                type:Number,
                default:3000
            },
            animDuration:{
                type:Number,
                Default:300
            },
            moveRatio:{
                type:Number,
                default:0.25
            },
            showIndicator:{
                type:Boolean,
                default:true
            },
        data:function(){
            return{
                slideCount:0,
                totalwidth:0,
                swiperStyle:{},
                currentIndex:1,
                scrolling:false
            }
        },
        methods:{
            startTimer:funxtion(){
                this.playTimer=windoe.seetInterval(()=>{
                    this.currentIndex++;
                    this.scrollCountent(-this.currentIndex*this.totalWidth);
                },this.interval)
            },
            stopTimer:function(){
                window.clearInterval(this.playTimer);
            },
...
                
//views/childComps/HomeSwiper.vue
<template>
    <swiper>
        <swiper-item v-for="item in banners">
            <a :href="item.link">
                <img :src="item.image" alt="" @load="imageLoad">
            </a>
        </swiper-item>
    </swiper>
</template>
<script>
    import {swiper,SwiperItem}from 'components/common/swiper'
    export default{
        name:"HomeSwiper",
        props:{
            default(){
                return []
            
            }
        },
        data(){
            return{
                isLoad:false
            }
        }
        components:{
            Swiper,
            SwiperItem
        },
        methods:{
            imageLoad(){
                if(!this.isLoad){
                    this.$emit('swiperImageLoad')
                    this.isLoad=true
                }
            }
        }
    }
</script>

9.推荐信息的展示

//home/childComps/RecommendView.vue
<template>
    <div class="recommend">
        <div v-for="item in recommends">
            <a :href="item.link">
                <img "src="item.image" alt="">
                <div>{{item.tittle}}</div>
            </a>
        </div>
    </div>
</template>
<script>
    export default{
        name:"RecommendView",
        props:{
            recommends:{
                type:Array,
                default(){
                    return[]
                }
            }
    }
</script>



<style scoped>
    .recommend{
        display:flex;
        width:100%;
        text-align:center;
        font-size:12px;
        padding:10px 0 20px;
        border-bottom:8px solid #eee;
    }
    .recommend-item{
        flex:1;
    }
    .recommend-item img{
        width:65px;
        height:65px;
        margin-bottom:10px;
    }
}

10.featureview的封装

//FeatureView.vue
<template>
    <div calss="feature">
        <a href="https://act.mogujie.com/zzlx67">
            <img src="~assets/img/home/recommend_bg.jpg" alt="">
        </a>
    </div>
</template>

<script>
    export default{
        name:"FeatureView"
    }
</script>
<style scoped>
    .feature img{
        width: 100%;
    }
</style>

11.TabControl 封装

//TabControl.vue
<template>
    <div class="tab-control">
        <div v-for="(item,index> in tittles"
            class="tab-control-item"
            :class="{active:index === currentIndex}" @click="itemClick(index)">
        <span{{item}}</span>
        </div>
    </div>
</template>
<script>
export default{
    name:"TabControl",
    props:{
        tittles:{
            type:Array,
            default(){
                return[]
            }
        }
    },
    data(){
        return{
            currentIndex:0
        }
    },
    methods:{
        itemClick(index){
            this.currentIndex=index;
            this.$emit('tabClick',index)
        }
    }
}
</script>
<style scoped>
    .tab-control{
        display:flex;
        text-align:center;
        font-size:15px;
        height:40px;
        line-height:40px;
    }
    .tab-control-item{
        flex:1;
    }

    .tab-control-item span{
        padding:5px;
    }

    .active{
        color:var(--color-high-text);
    }
    .active span{
        border-bottom:3px solid var(--color-tint);
    }
</style>

12.保存商品的数据结构设计

13.首页商品数据的展示

//conmmon/GoodList.vue
<template>
    <div class="goods">
        <goods-list-item v-for="item in goods" :goods-item="item"/>
    </div>
</template>

<script>
    import GoodsListItem from './GoodsListItem'

    export default{
        name:"GoodsList",
        components:{
            GoodsListItem
        },
        props:{
            goods:{
                type:Array,
                default(){
                    return[]
            }
        }
    }
}
</script>
//GoodsListItem.vue
<template>
    <div class="goods-item">
        <img :src="goodsItem.show.img" alt="" @load="imageLoad">
        <div class="goods-info">
            <p>{{class="goods-info"></P>
                <span class="price">{{goodsItem.price}}</span>
                <span class="collect">{{goodsItem.cfav}}</span>
        </div>
    </div>
</template>

<script>
    export default{
        name:"GoodsListItem",
        props:{
            goodsItem:{
                type:Object,
                default(){
                    return{}
                }
            }
        }
        methods:{
//图片加载
            imageLode:{
                this.$bus.$emit('itemImageLoad')
            }
    }
</script>  

14.better-scrollf封装和使用

better-scroll

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <tittle>Tittle</tittle>
    <style>
        .content>{
            height:200px;
            background-color:red;
            overflow:hidden;
        }
    </style>
</head>
<body>
<div>
    <div class="content">
        <ul>
            <li>niha</li>
            <li>niha</li>
            <li>niha</li>
            <li>niha</li>
        </ul>
    </div>
</div>
<scirpt src="./bscroll.js"></script>
<script>
    const bscroll=newBScroll(ducoment.querySelector('.contenr'),{
        probeType:3,
        Click:true,
        pullUpLoad:true
    })
    bscroll.on('pullingUp',()=>{
        console.log('上拉加载更多');
    
        setTimeout(()=>{
            bscroll.finishPullUp()
        },2000)
    })
</script>
//common/Scroll.vue
<template>
    <div class="wrapper" ref="wrapper">
        <div class="content">
            <slot></slot>
        </div>
    </div>
</template>

<script>
    import BScroll from 'better-scroll'
    export default{
        name:"Scroll",
       props:{
            probeType:{
                type:Number,
                default:0
            },
            oullUpLoad:{
                type:Boolean,
                default:false
            }
        }
        data(){
            return{
                scroll:null,
             }
        }
        mounted(){
//1.创建BScroll对象
            this.scroll=new BScroll(this.$refs.wrapper,{
                click:true,
                probeType:this.probeType,
                pullUpLoad:this.pullUpload
        })
//2.监听滚动的位置
            this.scroll.on('scroll',(position)=>
                this.$emit('scroll',position)
            })
//3.监听上拉事件
            this.scroll.on('pullingUp',()=>{
                this.$emit('pillingUp')
            })
        },
        methods:{
            scrollTo(x,y,time=300){
                this.scroll && this.scoll.scrollTo(x,y,time)
            },
            finishPullUp(){
                this.scroll.finishPullUp()
            },
            refresh(){
//防止scroll.vue还没有加载就调用refresh
                this.scroll && this.scroll.refresh()
            }
            finishPullUp(){
                this.scroll && this.scroll.finishPullUp()
            }
        }
    }
</script>

15.backtop的封装和使用

//BackTop.vue
<template>
    <div class="back-top" @click="backClick">
        <img src="assets/img/common/top.png" alt="">
    </div>
</template>
<script>
    export default{
        name:"BackTop",
        methods:{
            backClick(){
            

            }
        }
    }
</script>
<style scoped>
    .back-top{
        position:fixed;
        right:8px;
        bottom:55px;
    }
</style>

监听一个组件的原生事件,需要加native

16.上拉加载更多

scroll.vue/home.vue

非父子组件通信:http://www.jb51.net/artivle/132371.html

17.刷新频繁的防抖函数处理

debounce() home.vue里面

可以在common建立util再写debounce

18.tabControl的offsetTop获取分析

this.$refs.topTabControl.currentIndex=index;

this.$refs.tabControl.currentIndex=index;

让两个TabControl的currentIndex保持一致

 

19.记录离开时home的状态

//App.vue
<template>
    <div>
        <keep-alive<</keep-alive>
     </div>
</template>
//home.vue
computed:{
    activated(){
        this.$refs.scroll.scrollTo(0,this.saveY,0)
        THIS.$refs.scroll.refresh()
    },
    deactivated(){
        this.saveY=this.$refs.scroll.getScrollY
    },

20.跳转到详情页

//GoodListItem.vue
<template>
    <div class="goods-item @click="itemClick">

methods:{
    itemClick(){
        this.$router.push('/detail/'+this.goodsItem.iid)
}

//index.js
 {
    path:'/detail/:iid',
    component:Detail
 }

//detail.vue
<template>
    <div id="detail">
        <detail-nav-bar/ class="detailNav">
        <scroll class="content" ref="scroll">
        <detail-swiper :top-images="topImages"/>
        <detail-base-info :goods="goods"/>
        <detail-shop-info :shop="shop"/>
        <detail-goods-info :detail-info="detailInfo"/>
        <detail-param-info :param-info="paramInfo"/>
        <detail-comment-info :comment-info="commentInfo"/>
        <goods-list :goods="recommends"/>
        <detail=bottom-bar/>
        </scroll>
    </div>
</template>
<script>
import DetailNavBar from './childComps/DetailNavBar'
import DetailSwiper from './childComps/DetailSwiper'
import DetailBaseInfo from './childComps/DetailBaseInfo'
import DetailShopInfo frim './shildComps/DetailShopInfo'
import DetailGoodsInfo from './childComps/DetailGoodsInfo'
import DetailParamsInfo from './childComps/DetailParamInfo'
import DetailCommentInfo from './shildComps/DetailCommentInfo'
import DetailBottomBar from './childComps/DetailBottomBar'

import Scroll from 'components/common/scroll/Scroll

import {getDetail, Goods} from "network/detail";
export default{
    name:"Detail",
    components:{
        DetailNavBar,
        DetailSwiper,
        DetailBaseInfo,
        DetailShopInfo,
        Scroll,
        DetailGoodsInfo,
        DetailParamsInfo,
        DetailCommentInfo,
        DetailBottomBar
    },
    
    data(){
        return{
            iid:null,
            topImages:[]
            goods:{},
            shop{},
            detailInfo:{}
            paramInfo:{},
            itemParams:{},
            commentInfo:{}
        }
    },
    creater(){
//1.保存传入的iid
        this.iid=this.$route.params.iid
    }
//2.获取商品信息
        this.goods=new Goods(data.itemInfo,data.colume.data.shopInfo.services)
//3.创建店铺信息的对象
        this.shop=new Shop(data.shopInfa)
    })
//4.保存商品详情数据
        this.detailInfo=data.derailInfo;
    }
//5.获取参数的信息
        this.paramInfo=new GoodsParam(data.itemParams.info,data.itemParams.rule)
    })
//6.取出参数的信息
        this.itemParams=data.itemParams
//7.取出评论的信息
        if(data.rate.cRate != 0){
          this.commentInfo=data.rate.list[0]
        }
    },
    methods:{
        imageLoad(){
            this.$refs.scroll.refresh()
}
</script>
<style scoped>
    #detail{
        position:relative;
        z-indes:9;
        background-color:#fff;
    }
    .detail-nav{
        position:relative;
        z-indes:9;
        background-color:#fff;
    }
    .content{
        height:calc(100%-44px);
    }
</style>

21.详情页导航栏

//DetailNavBar.vue
<template>
    <div>
        <nav-bar>
            <div slot="left" class="back" @click="backClick"
            <div slot="center" class="tittle">
                <div v-for="(item,index) in tittles"
                    class="tittle-item :class="active:index===currentIndex} @click="tittleClick(index)"">
                {{item}}
                </div>
            </div>
        </nav-bar>
    </div>
</template>
<script>
    import NarVar from 'components/common/navbar/NavBar'
    export default{
        name:"DetailNavBar",
        components:{
            NavBar
        },
        data(){
            return{
                tittles:['商品','参数','评论','推荐'],
                currentIndex:0
            }
        },
        methods:{
            tittleClick(){
                this.currenIndex=index;
            },
            backClick(){
                this.$router.back()
            }
        }
    }
</script>
<style scoped>
    .tittle{
        display:flex;
        font-size:13px;
    }
    .tittle-item{
        flex:1;
    }
    .active{
        color:var(--color-high-text)
    }
    .back img{
        margin-top:12px;
    }
</style>

22.详情页请求数据以轮播图展示

//APP.vue
<keep-alive exclude="Detail">
    <router-view/>
</keep-alive>

//network/detail.js
import {request} from "./request";
export function getDetail(iid){
    return request({
        url:'/detail',
        params:{
            iid
        }
    })
}
//detail.vue

<script>
    import {{getDetail}} from"../../netwoek/detail";
</script>

    created(){
        getDetail(this.iid).then(res=>{
            console.log(res);
        })
    }

 

上面是数据请求

//detailSwiper.vue 轮播图
<template>
<div class="detail-swiper">
    <swiper class="swiper">
        <swiper-item v-for="item in topImags">
    </swiper>
<div>
<script>
import {Swiper,SwiperItem) from 'components/common/swiper'
export default{
    name:"DetailSwiper",
    components:{
        Swiper,
        SwiperItem
    },
    props:{
        toImages:{
            type:Array,
            default(){
                return[]
            }
        }
    }
}
</script>
<style scoped>
    .swiper{
        height:300px;
        overflow:hidden;
    }
</style>

23.详情页信息

//detail.js 数据
export function getRecommend(){
    return request({
        url:'/recommend'
    })
}
export class Goods{
    constructor(itemInfo.colums,services){
        this.tittle=itemInfo.tittle
        this.desc=itemInfo.desc
        this.newPrice=itemInfo.price
        this.oldPrice=itemInfo.oldPrice
        this.colums=colu,s
        this.service=services
        this.realPrice=itemInfo.lowNowPrice
    }
}
 export class Shop{
     constructor(shopInfo){
        this.logo=shopInfo.shopLoge;
        this.name=shopInfo.name;
        this.fans=shopInfo.cFans;
        this.sells=shopInfo,cSells;
        this.score=shopInfo.score;
        this.goodsCount=shopInfo.cGoods
    }
}

export class GoodsParam{
    constructor(info,rule){
        this.image-info.images?info.image[0] : '';
        this.infos=info.set;
        this.sizes=rule.tables;
    }
}
//DetailBaseInfo.vue 价格
<template>
    <div v-if="Object.keys(goods).length !== 0" class="base-info">
        <div class="info-price">
            <span class="n-price">{{goods.newPrice}}</span>
            <span class="o-price">{{goods.oldPrice}}</span>
            <span v-if"goods.discount" class="discount">{{goods.discount}}</span>
        </div>
        <div class="indo-service">
            <span class="info-service-item" c-for="indev in goods.services.length-1" :key="index">
            <img :src="goods.services[index-1].icon">
            <span>{{goods.services[index-1].name}}</spam>
        </span>
    </div>
  </div>
</template>
//DetailGoodInfo.vue 详细图片
<template>
    <div v-if="Object.key(detailInfo).length !== 0" class=:goods-info">
        <div class="info-desc clear-fix">
            <div class="start">
            </div>
            <div class="desc">{{detailInfo.desc}}</div>
            <div class="end"></div>
        </div>
        <div vlass="info-key">{{detailInfo.detailImage[0].key}}</div>
        <div class-"info-list">
            <img v-for="(item,index) in detailInfo.detailImage[0].list" :key="index" :src="item @load="imgLoad" alt-"">
        </div>
</templata>
<script>
    export default{
        name:"DetailGoodsInfo",
        props:{
            detailInfo:{
                type:Object
            }
        }
        data(){
            return{
                counter:0,
                imagesLenght:0
            }
        },
        methods:{
            imageLoad(){
//等所有加载完再回调一次
                if(++this.counter === this.imageLength){
                    this.$emit('imageLoad')
                }
            }
        },
        watch:{
            detailInfo(){
//获取图片个数
                this.imageLength = this.detailInfo.detailImage[0].list.length
            }
        }
    }
</script>
<style scoped>
    .goods-info{
        padding:20px 0;
        border-bottom: 5px solid #f2f5f8;
     }


</style>
//DetailParamInfo.vue 参数
<template>
<div class="param-info" v-if="Object.keys(paramInfo).length !==0">
    <table v-for="(table,index) in paramInfo.sizes"
        class="info-size" :key="index">
        <tr v-for="(tr indey) in table" :key="indey">
            <td v-for="(td,indez) in tr" :key="indez">{{td}}</td>        
        </tr>
    </table>
    <table class="info-param">
        <tr v-for="(info,index) in paramInfo.infos">
            <td class="info-param-key">{{info.key}}</td>
            <td class="param-value">{{info.value}}</td>
        </tr>
    </table>
    <div class="info-img"v-if="paramInfo.image.length !==0">
        <img :src="paramInfo.image" alt="">
    </div>
</template>
<script>
...
//DetailCommentInfo.vue 评论
<template>
...
</template>

<script>
    import {formatDate} from "/common/utils";
    export default{
        name:"DetailCommentInfo",
        props:{
            commentInfo:{
                type:Object,
                default(){
                    return{}
                }
            }
        },
        filters:{
            showDate(value){
              //1.将事件戳转成Date对象
                const date=new Date(value*1000)
              //2.将Date进行格式化
                return formatDate(date,"yyyy/MM/dd hh:mm:ss")
    }
</script>

<style scoped>
...
</style>

//GoodsListItem.vue 商品推荐
<template>
    <div class="goods-item" @click="itemClick">
        <img :src="showImage" alt="" @load="imageLoad">
        <div class="goods-info">
            <p>{{product.tittle}}</P>
            <span class="price">{{product.price}}</span>
            <span class="collect">{{product.cfav>></span>
        </div>
    </div>
</template>

<script>
ecport default{
    name:"GoodsListItem",
    props:{
        product:{
            type:Object,
            default(){
                return{}
            }
        }
    },
    computed:{
        showImage(){
            return this.product.image || this.product.show.img
        }
    methods:{
        imgLoad(){
            this.$bus.$emit('itemImageLoad')
        },
        itemClick(){
            this.$router.push({
                path:'/detail',
                query:{
                    iid:this.product.iid
                }
            })
        }
    }
</script>
..

mixin的使用

详情页导航栏的联动效果

//DetailNavBar.vue
data(){
    return{
        themeTopYs:[]
methods:{
    tittleClick(index){
        this.currentIndex=index;
        this.$emit('tittleClick',index),


//Detail.vue
<detail-nav-bar @tittleClick="tittleClick"/>

    <detail-params-info ref="params" :[aram-info="itemParams"/>
    <detail-comment-info ref="comment" :comment-info="commentInfo"/>
    <goods-list ref="recommend" :goods="recommends"/>

updated(){
       this.themeTopYs.push(0);
    this.themeTopYs.push(this.$refs.params.$el.offsetTop);
    this.themeTopYs.push(this.$refs.comment.$el.offsetTop);
    this.themeTopYs.push(this.$refs.recommend.$el.offsetTop)
//2.或者,等待渲染$el,但是图片还没加载,所以数据不对
//this.$nextTick(()=>{
//   this.themeTopYs.push(0);
//   this.themeTopYs.push(this.$refs.params.$el.offsetTop);
//  this.themeTopYs.push(this.$refs.comment.$el.offsetTop);
//  this.themeTopYs.push(this.$refs.recommend.$el.offsetTop)

//3.图片加载就给数据,加防抖

created(){
//给getThemeTopY赋值
    this.getThemeTopY=debounce(()=>{
        this.themeTopYs=[]
        this.themeTopYs.push(0)
        this.themeTopYs.push(this.$refs.params.$el.offsetTop);
        this.themeTopYs.push(this.$refs.comment.$el.offsetTop);
        this.themeTopYs.push(this.$refs.recommend.$el.offsetTop);
    },100)
},
methods:{
    detailImageLoad(){
        this.newRefresh()
        this.getThemeTopY()
    }
}

methods:{
    tittleClick(index){
        console.log(index);
        this.$refs.scroll.scrollTo(0,-this.themeTopYs[200],100)
    },

}
//Detail.vue 根据高度显示对应的主题
<detail-nav-bar @tittleClick="tittleClick" ref="nav"/>
coontentScroll(position){
    //1.获取y值
    const positionY=-position.y
    let length = this.themeTopYs.length
    //2.positionY和主题中值进行对比 index=0/1/2/3
    let length =this.themeTopYs.length
    for(let i =0; i<length; i++)
        if(this.current!=i &&(i<length-1 && positionY >this.themeTops[i] && positionY < this.themeTopYs[i] || (i ===length-1 && positionY > this.themeTopYs[i])){
            this.currentIndex=i;
            this.$ref.nav.currentIndex=this.currentIndex;
        }


//hack做法
加入最大值:this.themeTopTs.push(Number.MAX_VALUE)

let lenth=this.themeTopYs.length
for(let i=0; i<length-1; i++){
    if(this.currentIndex !== i &&(positionY >= this.themeTopYs[i] && positionY <this.themeTopYs[i+1])){
        this.currentIndex=i;
        this.$ref.nav.currentIndex = this.currentIndex
    }
}
//DetailBottomBar.vue 底部
<template>
    <div class="bottom-bar">
        <div>
            <i class="icon service"></i>
            <span class="text">客服<span>
        </div>
        <div>
            <i class="icon select"></i>
            <span class="text">收藏</span>
        </div>
        <div class="bar-item bar-right">
            <div class="cart" @click="addToCart">加入购物车</div>
            <div class="buy">购买</div>
        </div>
    </div>
</template>

<script>
    export default{
        name:"DetailBottomBar"
    }
</script>
<style scoped>
    .bottom-bar{
        height:49px;
        background:red;
       
        position:relative;
        bottom:49px;
    }
</style>
//backTop的混入安装 
//Detail.vue
//先导入插件和数据,变量,注册
    <back-top @click.native="backTop" v-show="isShowBackTop"/>
import {itemListenerMixin,backTopMixin} from "common/mixin";
export default{
    mixins:[itemListenerMixin,backTopMixin],
}
//mixin.js
export const itemListenerMixin={
    data(){
        return{
            itemImgListener:null,
            newRefresh:null
        }
    },
    mounted(){
        this.newRefresh=debounce(this.$refs.scroll.refresh,100)
        this.itemImgListener=()={
            this.itemImgListener=()=>
                this.newRefresh()
            }
            this.$bus.$on('itemImgLoad',this.itemImgListener)
        }
    }

export const backTopMixin={
    components:{
        BackTop
    },
    data(){
        return{
            isShowBackTop:false
        }
    },
    methods:{
        backTop(){
            this.$refs.scroll.scrollTo(0,0,300)
        },
        listenShoBack(position){
            this.isShowBackTop=-position.y>BACK_POSITION
        }
    }
}

 

//加入购物车
//DetailBottomBar.vue
<div class="cart @click="addToCart">加入购物车</div>
methods:{
    addToCart(){
        this.$emit('addCart'}
}
        
//Detail.vue
<detail-bottim-bar @addCart="addToCart"/>
method:{
    addToCart(){
        const product={}
            // 1.获取购物车需要展示的信息
            product.image=this.TopImages[0];
            product.tittle=this.goodsInfo.tittle;
            product.desc=this.goodsInfo.desc;
            product.price=this.goodsInfo.realPrice;
            procuct.iid=this.iid;

            //2.将商品添加到购物车
            this.$store.dispatch('addCart',product).then(res=>{
                this.$toast.show(res,2000);
            })
}
//加购
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'


//1.安装插件
Vue.use(Vuex)

//2.创建Store对象
const store=new Vuex.Store({
    stateList:[]
    
},
    mutations:{
        addCounter(state,payload){
            payload.count++
        }
        addToCart(state,payload){
            state.cartList.push(payload)
        }
          
}
    action:{
    addCart(context,payload){
//1.查找之前数组中是否有该商品
        let product=context.state.cartList.find(item=>item.iid === payload.iid)
//2.判断oldProduct
        if(product){
            context('addCounter',oldProduct)
        } else{
            context.commit('addToCart',payload)
        }

}
})

//3.挂在Vue实例
export default store

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。


//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip=false

//添加事件总线对象
Vue.prototype.$bus=newVue()

new Vue({
    render:h=>h(App),
    router,
    store
}).$mount('#app')

//将index 重构
//index.js
import Vue from 'vue'
import Vuex from 'vuex'

import mutations from './mutations'
import action from './actions'

//1.安装插件
Vue.use(Vuex)

//2.创建Store对象
const state ={
    cartList:[]
}

const store=new Vuex.Store({
    state,
    mutations,
    actions
})

//3.挂载Vue实例上
export default store

...................................

//mutation.js
import{
    ADD_COUNTER,
    ADD_TO_CART
}from './mutaion-types'

export default{

    [ADD_COUNTER](state,payload){
        payload.count++
    },
    [ADD_TO_CART](state,payload){
        state.cartList.push(payload)
    }
}

...................................

//action.js
import{
    ADD_COUNTER,
    ADD_TO_CART
} from './mutation-types'

export default{
    addCart(context,payload){
        return new Promise(resolve,reject) => {
//1. 查找之前数组中是否有该商品
            let oldProduct=context.state.cartList.find(item => item.iid ===payload.iid)
//2.判断oldProduct
        if(oldProduct){
            context.commit(ADD)COUNTER,oldProduct)
            resolve('当前的商品数量+1')
        }else{
            payload.count = 1
            context.commite(ADD_TO_CART,payload)
            resolve('添加了新的商品')
        }
      })
    }
}

......................................

//mutation.types.js
export const ADD_COUNTER='add_counter'
export const ADD_TO_CART='add_to_cart'
//cart.vue 购物车导航栏
<template>
    <div class="cart">
        <nav-bar class="nav-bar">
            <div slot="center">购物车({{length}})</div>
        </nav-bar>
    </div>
    <cart-list/>
    <cart-bottom-bar/>
</template>
<script>
    import NavBar from 'components/common/navBar/NavBar'
    import CartList from './shildComps/CartList'
    import CarBottomBar from './childComps/CartBottomBar'
    import {mapGetters} from 'vuex'

    export default{
        name:'Cart',
        components:{
            NavBar,
            CarBottomBar
        },
        computed:{
        //两种语法
            ...mapGetters{['cartLength','cartList'])
                length:'cartLength'
                List:'cartList'
        }
    }
</script>
<style scoped>
    .cart{
        height:100vh;
    }
    .nav-bar{
        background-color:var(--color-tint);
        color:#fff;
    }
</style>
.......................................

//getter.js
export default{
    cartLength(state){
        return state.cartList.length
    },
    cartList(state){
        return state.cartList(0
    }
}
//cartList 展示列表
<template>
//为加购再进入页面时刷新

    <div class="cart-list" ref="scroll">
        <scroll class=content">
            <cart-list-item v-for="(item,index) in cartList" :key="index" :product="item"/>
        </scroll>
    </div>
</template>
<script>
    import Scroll from 'components/common/scroll/Scroll'
    import CsrtListItem from './CartListItem'
    import {mapGetters} from 'vuex'
    export default{
        name: "CartList",
        components:{
            Scroll,
            CartListItem
        },
        computed:{
            ...mapGetters(['cartList'])
        }
//为加购再进入页面时刷新
        activated(){
            this.$refs.scroll.refresh()
        }
    }
</script>
<style scoped>
    .cart-list{
        height:cart(100%-44px-49px-40px);
    }
    .content{
        height:100%;
        overflow:hidden
    }
</style>
........................................

//CartListItem 具体展示每一个数据

<template>
    <div id="shop-item">
        <div class="item-selector">
            <checkButton @checkBtnClick="checkedChange" :value="itemInfo.checked" @click.native="checkClick"></checkButton>
        </div>
        <div class="item-img">
            <img :src="itemInfo.image" alt="商品图片">
        <div>
        <div class="item-info">
            <div class="item-tittle">{{itemInfo.tittle}}</div>
            <div class="item-desc">{{itemInfo.desc}}</div>
            <div class="item-count right">{{itemInfo.count}}</div>
            <div class="info-button">
                <div class="item-price left">¥{{itemInfo.price}}</div>
                <div class="item-count right">{{itemInfo.count}}</div>
            </div>
        </div>
    </div>
</template>

<script>
    import CheckButton from './CheckButton'
    export deafult{
        name:"CartListItem",
        props:{
            product:{
                type:Object,
                default(){
                    return{}
                }
            }
        }
        methods:{
            checkClick(){
                this.itemInfo.checked= !this.itemInfo.checked
            }
        }
    }
</script>
<style scoped>
 #shop-item{
    width:100%;
    display:flex;
    fon-size
...
</style>
//CheckButton.vue 小选中按钮
<template>
    <div class="check-button" :class="{check:isChecked}">
        <img src="~assert/img/cart/tick.svg" alt="">
    </div>
</template>
<script>
    export default{
        name:"CheckButton",
        props:{
            isChecked:{
                type:Boolean,
                default:false
            }
        }
    }
</script>
<style scoped>
    .check-button{
        border-radius:50%;
        border: 2px solid #aaa;
    }
    .check{
        border-coloe:red;
    }
</style>

//设置是否选中
//mutation.js
 [ADD_TO_CART](state,payload){
     payload.checked=true
    state.cartList.push([ayload)
    }
//CartListItem.vue
<CheckButton :is-checked="itemInfo.checked"/>
//CarBottomBar.vue 商品底部结算
<template>
    <div class="bottom-bar">
        <div class="check-content">
            <check-button :is-checked="isSelecAll" class="check-button" @click.native="checkClick"/>
            <span>全选</span>
        </div>

        <div class="pricr">
            合计:{{totalPrice}}
        </div>
    
        <div class="calculate">
            去计算{{checkLength}}
        </div>
    </div>
</template>

<script>
    import CheckButton from 'components/content/checkButton/checkButton'
    export default {
        name:"CartBottomBar",
        components:{
            CheckButton
        },
        computed:{
            totalPrice(){
            return '¥' +this.$tore.state.cartList.filter(item=>{
                return item.checked
            }).reducce((preValue,item)=>{
                return preValue+item.price*item.count
            },0).toFixed(2)
            }
            checkLength(){
            return this.$store.state.carList.filter(item=>item.checked).length
            },
            isSelectAll(){
            //return !(this.cartList.filter(item=>!item.checked).length)
                if (this.cartList.length ===0 ) return false
                return !this.cartList.find(item => !item.checked)
            },
        methods: {
            checkClick(){
                if(this.isSelectAll){
                    this.cartList.foreach(item => item.checked=false)
                }else{
                    this.cartList.forEach(item => item.checked=true)
                }
            },
            calcClick(){
                if(!this.isSelectAll){
                    this.$toast.show('请选择购买的商品',2000)
                }
            }

    }
</script>
<style scoped>
    .bottom-bar {
        height:40px;
        backgroung-color:red;
        position:relative;
        display:flex;
        line-height:40px;
    }
    .check-content {
        display:flex;
        align-items:center;
        margin-left:10px;
    }
    .check-button {
        weidth:20px;
        height:20px;
        line-height:20px;
        margin-right:5px
    }
    .price{
        margin-left:20x;
    }
    .calculate{
        width:90px;
        background:red;
        color:#fff;
        text-align:center
    }
</style>
//封装toast
//Toast.vue
<template>
    <div class="toast" v-show="show">
        <div>{{message}}</div>
    </div>
</template>
<script>
    export default {
        name:"Toast",
        data(){
            message:'',
            isShow:false
        }
    },
    methods:{
        show(message='默认文字',duration=2000){
            this.isShow=true;
            this.message=message
            setTimeout(() => {
                this.isShow=false;    
                this.message =''
            },duration)
        }
    }
    }
</script>
<style scoped>
    .toast{
        position:fixed;
        top:%;
        left:%;
        transfrom:translate(-50%,-50%);
        padding:8px 10px;
        
        z-index:999;        

        color:#fff;
        background-dolor:rgba(0,0,0,.75);
    }
</style>
//................................................................................
//toast/index.js
import Toast from './Toast'
const obj={}
obj.install=function(Vue){
    //1.创建组件构造器
    const toastConstrustor=Vue.extend(Toast)

    //2.new的方式,根据组件构造器,可以创建出一个组件对象
     const toast=new toastConstrustor()

    //3.将组件对象手动挂载到某一个元素上
    toast.$mount(document.createElement('div'))

    //4.toast.$el对应的就是div
    document.body.appendChild(toast.$el)

    Vue.prototype.$toast=Toast;
}

//..........................................................................

//main.js
import toast from 'componnets/common/toast'
//安装toast插件
Vue.use(toast)

//........................................................................
//CartbottomBar.vue
methods:{
    calcClick(){
        if(!this.isSelectAll){
            this.$totast.show('请选择购买的商品',2000)
        }
    }
}



fastclick 解决移动端300毫秒的延迟

polifill:做适配

npm install fastclick --save

//main.js
import FastClick from 'fastclick'
FastClick.attch(document.body)

vue-lazyload框架 图片懒加载

 

npm install vue-lazyload --save

//main.js
import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyLoad,{
    loading:require(./assets/img/common/placeholder.png')
})

使用时: <img v-lazy="showImage" alt="" @load="imgLoad">

p2vw css单位转化插件

npm install postcss-px-to-viewport --save-dev

//postcss.config.js 对插件进行配置
module.exports={
    plugins:{
        autoprefixer: {},
            "postcss-px-to-viewport": {
                viewportWidth:375, //视窗的宽度,对应的时我们设计稿的宽度
                viewportHeight:667, //视窗的高度,对应的是我们设计稿的高度(也可以不配置))
                unitportUnitL'vw', //指定’px'转化为私闯单位,建议使用vw
                selectorBlackList:['ignore','tab-bar','tab-bar-item'], 
//指定不需要转换的类,
                minPixelValue:1, //小于或等于‘1px'不转换为视窗单位
                mediaQuery:false  //允许在媒体查询中转换'px'
    }
}

nginx - 项目在window的部署

打包:npm run build

https://nginx.org

ubuntn

响应式原理-依赖技术的分析和学习

发布订阅者模式

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值