Vue

目录

一.Vue介绍

1.前端发展史

1.HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端渲染完数据 -> 返回数据给前端 -> 在浏览器中查看

2.Ajax的出现 -> 后台发送异步请求,Render+Ajax混合

3.单用Ajax(加载数据,DOM渲染页面):前后端分离的雏形

4.Angular框架的出现(1个JS框架):出现了“前端工程化”的概念(前端也是1个工程、1个项目)

5.ReactVue框架:当下最火的2个前端框架(Vue:国人喜欢用,React:外国人喜欢用)

6.移动开发(Android+IOS) + Web(Web+微信小程序+支付宝小程序) + 桌面开发(Windows桌面):前端 -> 大前端

7.一套代码在各个平台运行(大前端):谷歌Flutter(Dart语言:和Java很像)可以运行在IOS、Android、PC端

8.在Vue框架的基础性上 uni-app:一套编码 编到10个平台

9.主流框架:Vue,React ,uni-app JavaScript typescript

详细前端发展史:https://zhuanlan.zhihu.com/p/337276087?utm_source=wechat_session&utm_medium=social&utm_oi=41967790587904

2.Vue简介

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架

与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用

使用Vue框架,可以完全在浏览器端渲染页面服务端只提供数据

Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合

渐进式框架

可以一点一点地使用它(局部使用),只用一部分,也可以整个工程都使用它

网站

3.Vue特点

易用

  • 通过 HTML、CSS、JavaScript构建应用

灵活

  • 不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩。

高效

  • 20kB min+gzip 运行大小
  • 超快虚拟 DOM
  • 最省心的优化
# Vue与React的区别
    React与Vue 都采用虚拟DOM
    核心功能都在核心库中,其他类似路由这样的功能则由其他库进行处理
    React的生态系统更庞大,由ReactNative来进行混合App开发; Vue更轻量
    React由独特的JSX语法; Vue是基于传统的Web计数进行扩展(HTML、CSS、JavaScript),更容易学习
    
# Vue与Angular的区别
        Angular1和Angular2以后的版本 是完全不同的两个框架; 一般Angular1被称作Angular.js, Angular之后的版本被称作 Angular
        Vue与Angular的语法非常相似
        Vue没有像Angular一样深入开发,只保证了基本功能。 Angular过于笨重
        Vue的运行速度比Angular快得多
        Angular的脏检查机制带来诸多性能问题

4.M-V-VM思想

① MVVM介绍

MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,是一种事件驱动编程方式

  • MModel :vue对象的data属性里面的数据,这里的数据要显示到页面中,数据层(js)
  • VView :vue中数据要显示的HTML页面,在vue中,也称之为“视图模板视图层 (HTML+CSS)
  • VMViewModel: vue做出来的介于M和V之间的一层,以后不需要手动进行dom操作了对数据监听双向数据绑定:JS中变量变了,HTML中数据也跟着改变),也就是vue加入的一层

image

② MVVM的特性

  • 低耦合视图(View)可以独立于Model变化和修改,1个ViewModel可以绑定到不同的View上,当View变化的时候 Model可以不变,当Model变化的时候 View也可以不变
  • 可复用:可以把一些视图逻辑放在1个ViewModel中,让很多View重用这端视图的逻辑(以此减少代码冗余)
  • 独立开发开发人员可以专注于业务逻辑数据的开发(ViewModel),设计人员可以专注于页面设计
  • 可测试:界面元素是比较难以测试的,而现在的测试可以针对ViewModel来编写

5.组件化开发、单页面开发

组件化开发

类似于DTL中的include每一个组件的内容都可以被替换复用,有自己独立的html,css,js,以后都是写出一个个组件,组件的组合成页面

单页面应用(spa)

只需要1个页面,结合组件化开发来替换页面中的内容

页面的切换只是组件的替换,页面还是只有1个index.html,里面就内容都套在一个div

6.版本

1.X:使用得较少

2.X:普遍使用(最多)

3.X:公司里新项目会选择

# 语法有差距,但是vue3完全兼容vue2

可以在vue3上写vue2 但是'官方不建议'

7.引入方式

① CDN的方式引入

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

下载后导入

下载后保存为js文件导入:直接在浏览器中打开https://cdn.jsdelivr.net/npm/vue/dist/vue.js,然后复制下来,创建一个js文件夹>创建vue文件,再粘贴进去

<script src="js/vue.js"></script>

image

8.选择什么编辑器开发前端

公司中常用:

  • webstorm和pycharm差不多

  • eclipse:免费,my eclipse:收费

  • jetbrains系列:捷克公司,最早出了一款编辑器[IDEA],是用java开发做java开发的。

    • 开发java:捷克做出来IDEA
    • 开发Python:捷克做出来pycharm
    • 开发go:捷克做出来goland
    • 开发php:捷克做出来phpstorm
    • 开发前端:捷克做出来webstorm
  • vscode:微软公司,免费开源轻量级,前端人员用的多,可开发python、go..

  • sublime text:前端人员用的多

  • vim:小众的

这里我们用pycharm+Vue插件来讲解专业的前端不会用这种方式

9.模板语法

语法不提示颜色问题

settings>>Editor>>Plugins>>下载一个vue插件即可

安装后一定要重启区别仅仅是没有提示而已

1)简单使用

1.新建一个html页面,把下载后的vue导入

<head>
        <script src="js/vue.js"></script>
           <!-- 或(两种引入方式)-->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

2.单页面开发:在body中包一个div,把vue所有东西写在div中,配置项可写在外面

id是# class是.

<body>
<div id="app">
    姓名:{{name}}
    <br>    <!--换行-->	
    年龄:{{age}}
</div>
    <script>
        //配置项:k:v传
        let vm=new Vue({
            el:'#app',  //vue管理这个div,以后在div中就可以写vue的语法了
            data:{
                name:'zy',
                age:19
            }
        })
    </script>
</body>

2)双向数据绑定测试

image

# 在页面console中就可以用vm去修改js中变量的值:

vm._data.name='帅哥'    // 可用_data.name

vm.name='帅哥'    // 也可直接.name  建议※

image

3)插值语法

被Vue托管的标签中可以在{{}}中写:变量js简单表达式函数三目运算符,这种写法就叫插值语法

需注意:插值不能写在标签的属性上,只能写在标签内部

<body>
    <div id="app">
        <h1>插值语法</h1>
        <p>姓名:{{name}}</p>
        <p>年龄:{{age}}</p>

        <p>爱好:{{hobby}}</p>
        <p>第一个爱好:{{hobby[0]}}</p>

        <p>学校信息:{{info}}</p>
        <p>学校名:{{school_info.name}}或{{school_info['name']}}</p>

        <p>标签:{{a_url}}</p> <!--默认不会渲染成标签-->

        <p>运算:{{10*2+3*4}}</p>
        <p>三目运算符【条件?'符合':'不符合'】:{{10>90?'大于':'小于'}}</p>
        <p>也可以带函数:后面再讲</p>
    </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',  //管理id是app的div
        data:{
            name:'zy',
            age:'18',
            hobby:['唱','跳','rap'],
            school_info:{name:'家里蹲',phone:110},
            a_url:'<a href="http://www.baidu.com">点击进入百度</a>'  //默认不会渲染成标签
        }
    })
</script>

image

标签问题:默认不会渲染成标签,如果想渲染成标签则需要用到指令

10.指令

vue提供的都是v-xx 写在标签属性上的统称为指令

假如要用name变量名,那该变量名必须是data定义过的变量

1)文本指令

文本指令 释义
v-html 把字符串的内容渲染成标签
v-text 把字符串内容渲染在标签内
v-show 放1个布尔值:为true 标签就显示;为false 标签就隐藏
v-if 放1个布尔值:为true 标签就显示;为false 标签就删除
v-html与v-text

用链接举例

<body>
    <div id="app">
        <p v-html="a_url"></p>

        <p v-text="a_url"></p>
    </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',//管理id是app的div
        data:{
            a_url:'<a href="http://www.baidu.com">点击进入百度</a>'
        }
    })
</script>

image

其实v-text就是把<>转成转义字符:<是&lt;,>是&gt;

v-show与v-if(显示/隐藏 与 显示/删除)

v-show与 v-if的区别:

v-show:true或false标签都在,只是加了display: none隐藏了
v-if:直接操作DOM,显示/删除 标签

用图片举例

v-show:显示/隐藏内容

<body>
    <div id="app">
        <h1>v-show:显示/隐藏内容</h1>
        <img v-show="img" src="图片地址" alt="" width="300px">
    </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',  //管理id是app的div
        data:{
            img:true  //true显示   false隐藏
        }
    })
</script>

image

image

v-if:显示/删除内容

<body>
    <div id="app">
        <h1>v-if:显示/删除</h1>
        <img v-if="img" src="图片地址" alt="" width="300px">
    </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',  //管理id是app的div
        data:{
            img:true  //true显示   false删除
        }
    })
</script>

image

image

2)事件指令

事件指令是:点击事件、双击事件、滑动事件..(用的最多的是点击事件click

事件指令 释义
v-on 触发事件(不推荐)如:v-on:事件名="函数名"
@ 简写(推荐)如:@事件名="函数名"
@事件名='函数名'

举例:给图片添加一个按钮,点击后可显示或隐藏图片

给按钮添加点击事件,点击事件函数必须写在methods

<body>
        <div id="app">
                <button @click="look">点我看图片</button>
                <div v-show="show"> <!--让图片先不显示,等添加点击事件-->
                    <img src="图片地址" alt="" height="200px" width="200px">
                </div>
        </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',  //管理id是app的div
        data:{
            show:true
        },
        // 函数都要放在methods配置项中,当点击按钮就会触发look函数的执行
        methods:{
            'look':function (){  //匿名函数
                //this就是当前vue实例:vm实例
                this.show=!this.show //!this.show(取反):如果show是true就是false
            }
        }
    })
</script>

image

以上点击显示或不显示图片举例需了解:

1.给按钮中的点击事件:<button @事件名="函数名">是简写方式,以后都要用这种
2.点击事件函数必须写在methods
3.this就是当前vue实例:vm实例
4.this.show=!this.show vm.show=!vm.show 取反(如果show是turn就是false)

3)属性指令

标签上的属性可以绑定变量,以后变量变化,标签上的属性也跟着变化

1)写在标签上的,如:name、class、id、src..都是属性

2)用法:v-bind:属性名='变量'

3)可简写::属性名='变量'

属性指令 释义
v-bind 直接写js的变量或语法(不推荐)
: 简写(以后都用该方式)
:属性名='变量'
<body>
    <div id="app">
        <a :href="url">点击看百度</a>  <!--简写方式,建议使用【:属性="变量名"】-->
    </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',//管理id是app的div
        data:{
            url:'http://www.baidu.com'
        }
    })
</script>

image

以上使用一个链接属性去举例子,下面再用图片链接+长+宽属性来举例子:(其实都一样)

<body>
    <div id="app">
        <img :src="photo" alt="" :height="h" :width="w">
    </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',  //管理id是app的div
        data:{
            photo:'图片地址',
            h:'200px',
            w:'200px'
        }
    })
</script>

image

总结:属性指令指的是所有属性,只要是属性都可以用一个变量去绑定值

4)案例

(1)点击按钮随机切换图片
<body>
        <div id="app">
                <button @click="look">点击随机切换图片</button>
                <div>
                        <img :src="url" alt="" height="200px" width="200px">
                </div>
        </div>
</body>
<script>
    var vm = new Vue({
        el:'#app',  //管理id是app的div
        data:{
            url:'图片地址',
            //定义一个数组,里面放多张图片,当点击按钮时从里面随机出一张图片
            url_list:[
                    '图片地址',
                    '图片地址',
                    '图片地址',
                    '图片地址',
            ]
        },
        methods:{
            'look':function (){
              //取一个0~N(取整(列表数据个数)之间的随机数
              var i =Math.round(Math.random()*(this.url_list.length-1))
              this.url=this.url_list[i]
            }
        }
    })
</script>

image

(2)点击开始后每隔1s换一个图片

1.需setInterval(函数, 毫秒)计时器 每n毫秒后执行一次代码

2.this指向问题:如果函数中再套函数,内部不是箭头函数this指向会不对,需外部var _this = this 重新定义以下,以后用_this就代表vm实例

3.学会es6对象写法,以后基本都是该形式

<body>
        <div id="app">
                <button @click="look">点击随机切换图片</button>
                <div>
                    	<img :src="url" alt="" height="200px" width="200px">
                </div>
        </div>
</body>
<script>
    var vm = new Vue({
        el: '#app',  //管理id是app的div
        data:{
            url:'图片地址',
            //定义一个数组,里面放多张图片,当点击按钮时从里面随机出一张图片
            url_list:[
                    '图片地址',
                    '图片地址',
                    '图片地址',
                    '图片地址',
            ]
        },
        methods:{
            'look': function () {
                var _this = this
                // 计时器
                setInterval(function () {
                    //查看下_this和this现在分别指向谁
                    console.log(_this) //vm实例
                    console.log(this) //Window
                    //取一个0~N(取整(列表数据个数)之间的随机数
                    var i = Math.round(Math.random() * (_this.url_list.length - 1))
                    _this.url = _this.url_list[i]
                },1000)
            }
        }
    })
</script>

image

简写点击事件:es6的对象写法

<!--用了es6对象写法 代码还非常精简-->
<script>    
      methods: {
            look() {
                var _this = this
                setInterval(function () {
                    var i = Math.round(Math.random() * (_this.url_list.length - 1))
                    _this.url = _this.url_list[i]
                }, 1000)
            }
        }
</script>
了解:简写es6对象写法
<script>  
    var name = 'zy'
    var hobby = ['篮球', '足球']
    var f = function () {
        console.log('匿名函数')
    }
    var d = {name, age: 18, hobby, f}  //es6可以直接写变量名、函数名(里面对应着值)
    console.log(d)
</script>

image

(3)点击开始随机切换图片,点击图片停止并弹出地址

11.class和style

class和style本身都是属性,但是较为特殊就单独拎出来讲

数据的绑定

# class:推荐用【数组】
    :class='变量'   
    变量可以是字符串、数组(列表)、对象(字典)
    
# style:推荐用【对象】
    :style='变量'   
    变量可以是字符串、数组(列表)、对象(字典)

class

<head>
    <style>
        .font {
            font-size: 60px;
        }
        .font-color {
            color: #4cae4c;
        }
        .red {
            background-color: red;
        }
    </style>
</head>

<body>
<div id="app">
        <h1>class</h1>
        <div :class="class_str">我是class的字符串形式</div>
        <div :class="class_obj">我是class的对象(字典)形式</div>
        <div :class="class_list">我是class的数组(列表)形式</div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',  //管理id是app的div
        data: {
            class_str:'font',
            //font-color因为中间有-必须要用引号引起来
            class_obj:{font:true,red:false,'font-color':true},
            class_list:['font','red','font-color']  // 推荐使用数组形式
        },
    })
</script>

image

# 字符串修改
vm.class_str='red'  //把字符串里的变量改为一个
vm.class_str='red font'  //把字符串里的变量改为两个

# 对象修改
vm.class_obj.red=true   //把对象里的变量改为true
vm.class_obj['red']=false   //把对象里的变量改为false

# 数组修改 (数组还有其他额外方法)
vm.class_list.pop() //删除尾部
vm.class_list.push('font-color') //尾部新增

修改

image

image

style

<body>
        <div id="app">
            <h1>style</h1>
            <div :style="style_str">我是style的字符串形式</div>
            <div :style="style_list">我是style的数组(列表)形式</div>
            <div :style="style_obj">我是style的对象(字典)形式</div>
        </div>
</body>
<script>
    var vm = new Vue({
        el: '#app',  //管理id是app的div
        data: {
            style_str:'font-size:60px',
            // style_list:[{'font-size':'60px'},{color:'red'}],
            //可以用驼峰式就不用加双引号
            style_list:[{fontSize:'60px'},{color:'red'}],
            style_obj:{fontSize:'60px',color:'red'}//推荐使用
        },
    })
</script>

image

修改

# 字符串修改
vm.style_str='color:red'  //把字符串里的变量改为一个
vm.style_str='color:red;font-size:60px' //把字符串里的变量改为两个【注意分号分隔】

# 数组修改 (数组还有其他额外方法)
vm.style_list[0].fontSize='40px' //把索引0的属性修改
vm.style_list.pop() //删除尾部

# 对象修改
vm.style_obj.color='blue'  //修改对象里的属性值
vm.style_obj['color']='red'  //修改对象里的属性值

二.Vue基础

1.条件渲染

指令 释义
v-if 相当于:if
v-else-if 相当于:else if
v-else 相当于:else

v-if=条件 放在标签上,如果条件成立该标签就显示,如果条件不成立该标签就不显示
v-else-if=条件 放在标签上,如果条件成立该标签就显示,如果条件不成立该标签就不显示
v-else 放在标签上,上面条件都不成立显示这个标签

举例:成绩大于等于90为优秀,大于等于60及格,小于60不及格

<body>
        <div id="app">
                <h1>条件渲染,根据分数显示文字</h1>
                <div v-if="score>=90">优秀</div>
                <div v-else-if="score>=60&&score<90">及格</div>
                <div v-else>不及格</div>
        </div>
</body>
<script>
    var vm = new Vue({
        el: '#app',  //管理id是app的div
        data: {
            score: 99
        },
    })
</script>

image

2.列表渲染

想让哪个标签循环 就把v-for放在哪个标签上

指令 释义
v-for 放在标签上可循环显示多个此标签

1)举例:点击按钮显示购物车,不点击就不显示

<body>
<div id="app">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <div class="text-center">
                    <button @click="clinkshopcar" class="btn btn-danger">[显示/关闭]购物车</button>
                </div>
                <div v-if="show">
                    <table class="table table-hover">
                        <thead>
                        <tr>
                            <th>id</th>
                            <th>商品</th>
                            <th>价格</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr v-for="item in good_list">
                            <th scope="row">{{item.id}}</th>
                            <td>{{item.name}}</td>
                            <td>{{item.price}}</td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',  //管理id是app的div
        data: {
            show: false,
            good_list: [
                {id: 1, name: '牛肉', price: '200元'},
                {id: 2, name: '鸡肉', price: '100元'},
                {id: 3, name: '羊肉', price: '300元'},
            ]
        },
        methods: {
            clinkshopcar() {
                this.show = !this.show
            },
        }
    })
</script>

image

2)v-for可以循环的变量

字符串数字数组(列表)对象(字典)

(1)字符串与数字
<body>
<div id="app">
    <!--字符串-->
    <h3>循环字符串(空格也会被循环但是div不显示)</h3>
    <div v-for="item in s1">{{item}}</div>
    <h3>循环字符串(循环并判断如果是空格就换行)</h3>
    <div v-for="item in s1">
        <p v-if="item!=' '">{{item}}</p>
        <br v-else>
    </div>
    <h3>循环字符串(带索引)</h3>
    <div v-for="(item,index) in s1">索引:{{index}}    字符:{{item}}</div>
    
    <!--数字-->
    <h3>循环数字(5就是1~5)</h3>
    <div v-for="item in 5">{{item}}</div>
    <h3>循环数字带索引</h3>
    <div v-for="(item,index) in 5">索引:{{index}}    数字:{{item}}</div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            s1:'hi dog'
        },
    })
</script>

image

(2)数组与对象
<body>
<div id="app">
    <!--数组-->
    <h3>循环数组</h3>
    <div v-for="item in l1">{{item}}</div>
    <h3>循环数组带索引</h3>
    <div v-for="(item,index) in l1">索引:{{index}}    数组:{{item}}</div>

    <!--对象-->
    <h3>循环对象(value)</h3>
    <div v-for="item in d1">{{item}}</div>
    <h3>循环对象(key+value)</h3>
    <div v-for="(item,key) in d1">key:{{key}}    value:{{item}}</div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            l1: [1, 2, 3],
            d1: {name: 'zy', age: 18},
        },
    })
</script>

image

补充:js的循环方式

image

①js基于索引的循环

python只有基于迭代的循环

<script>
    var a = [1, 2, 3]
    //a.length 长度
    for (i = 0; i < a.length; i++) {
        //基于索引循环打印数组中的值
        console.log(a[i])
    }
</script>
②js基于迭代的循环
<script>
    # 在js中如果用in拿到的只是数组中的索引值
    //var a = [1, 2, 3]
    //for (i in a) {
        //console.log(i)
    ————————————————
    var a = [1, 2, 3]
    for (i in a) {
        //其实还是基于索引取值
        console.log(a[i])
    }
</script>
③es6语法的of循环
<script>
    var a = [1, 2, 3]
    for (i of a) {
        console.log(i)
    }
</script>
④额外:数组的独有方法:forEach循环
<script>
    var a = [1, 2, 3]
    a.forEach(function(item){
        console.log(item)
    })
</script>
⑤额外:jq 的循环 循环数组,对象
<script>
    var a = [1, 2, 3]
    //可打印出索引和值 第一个索引,第二个值
    $.each(a,function (index,item){
        console.log('索引:',index,'值:',item)
    })
</script>

image

3)key值的解释

vue的v-for写在标签上,在标签上加一个key,用属性指令绑定一个变量,key的值每次都不一样,这样可以加速虚拟dom的替换,从而提高循环效率,且key值必须唯一

假如要给14中间插一个8进去,如果不加key就会把14重新写一遍中间加上8。如果加了key那么1~4不动,只会在中间插一个8。主要用来提高循环效率。其实对后端来说感受不出来快慢。对专业的前端来说都会写

key可以理解为是给每一个值加一个唯一标号

举例:
<script>
        //key用属性指令绑定
        <div v-for="item in 8" :key="item">{{item}}</div>
</script>      

4)数组、对象的检测与更新

当给一个对象新增一个keyvalue,会发现页面没有变化!这是因为vm这一层会检测一些方法,只要检测到变动就会更新,但是有些方法检测不到!!!

所以当给对象数据变动但页面没动就用: Vue.set(this.info, 'hobby', '篮球') 设置即可有变化

数组数据变动但页面没动同样也用:Vue.set(this.要动的数组,'数组索引','数组值')

<body>
<div>
    <div id="app">
        <h1>循环对象</h1>
        <div v-for="(value,key) in info">
            <h3>key值是:{{key}}</h3>
            <h2>value是:{{value}}</h2>
            <hr>
        </div>
        <button @click="add">点我,加数据</button>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            info: {name: 'zy', age: '18'},
        },
        methods: {
            add() {
                // 当k存在修改时:页面上会马上发生变化
                // this.info['name'] = '帅哥'

                // 当k不存在新增时:页面不会发生变化但是有值
                // this.info['hobby'] = '唱'
                // 建议用法:
                Vue.set(this.info,'hobby','篮球')
            }
        }
    })

image

image

可以检测到变动的数组操作

push:#最后位置添加
pop:#最后位置删除
shift:#第一个位置删除
unshift:#第一个位置添加
splice:#切片
sort:#排序
reverse:#反转

检测不到变动的数组操作

filter():#过滤
concat():#追加另一个数组
slice():
map():

# 原因:作者重写了相关方法(只重写了一部分方法,但是还有另一部分没有重写)

3.input事件

# input 的事件:
	click    #点击输入框时,触发的事件
    input	#当输入框进行输入时,触发的事件
    change	#当里面的值发生改变时,触发的事件
    blur	#当输入框失去焦点时,触发的事件
    focus   #当获得焦点时,触发事件
<body>
<div id="app">
  <h1>click点击输入框触发</h1>
  <input type="text" @click="handleClick">

  <h1>blur输入框失去焦点触发</h1>
  <input type="text" @blur="handleBlur">

  <h1>input输入框输入时触发</h1>
  <input type="text" @input="handleInput">

  <h1>change输入框的内容改变时触发</h1>
  <input type="text" @change="handleChange">

  <h1>focus输入框获得焦点触发</h1>
  <input type="text" @focus="handleFocus">

</div>
</body>
<script>
  var vm = new Vue({
    el: '#app',
    data: {},
    methods:{
      handleClick(){
        alert('点击了输入框')
      },
      handleBlur(){
        alert('输入框失去了焦点')
      },
      handleInput(){
        alert('输入框输入了内容')
      },
      handleChange(){
        alert('输入框中内容改变了')
      },
      handleFocus(){
        alert('输入框获得了焦点')
      }
    }
  })
</script>

image

4.v-mode数据的双向绑定

只针对input框做双向数据绑定

要求:input框中输入值后要被js变量拿到

如果使用 :value='变量' 这种形式:输入框变化,变量不会变

如果使用v-model做双向数据绑定:输入框变化,变量跟着变

<body>
<div id="app">
    <h1>数据的单向绑定</h1>
    <input type="text" :value="name">您输入的内容是:{{name}}
    <h1>数据的双向绑定</h1>
    <input type="text" v-model="name">您输入的内容是:{{name}}
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
          name:''
        },
    })
</script>

image

image

5.事件处理

1)过滤案例

扩展知识
①数组的过滤方法.filter

每次从数组中取出一个值给item执行下面的代码直到循环结束,返回true/false决定数据要或不要

<script>
    var l1=[1,2,3]
    var newl1 = l1.filter(function(item){
        return false  // true表示这个值保留,false表示这个值不要
    })
    console.log(newl1)
</script>
——————————————————
可简写为箭头函数:
<script>
    var l1=[1,2,3]
    var newl1 = l1.filter((item)=>{return false  })  // true表示这个值保留,false表示这个值不要
    console.log(newl1)
</script>

image

②字符串的indexOf方法

判断子字符串是否在当前字符串中,如果在返回的是索引,如果不在返回-1

<script>
    var s1='xyz'
    var s2='z'
    var i = s1.indexOf(s2)
    console.log(i)
</script>

image

③es6 的箭头函数写法

当函数中套函数时this指向会有问题,如果用箭头函数this指向就不会出问题因为:箭头函数没有自己的this,用的都是上一级的this

<script>
    var f = function () {
        console.log('函数')
    }
    //把上面的变成箭头函数
    var f = () => {
        console.log('函数')
    }
    ——————————————————
    // 1.无参数,无返回值的箭头函数
    var f = () => {
        console.log('函数')
     }
    f()
    
    // 2.有1个参数,没有返回值的箭头函数【括号可以去掉,也可以加】
    var f = item => {
        console.log(item)
    }
    f('zy')
    
    // 3.有多个参数,没有返回值的箭头函数【括号不能去掉】
    var f = (item, key) => {
        console.log(item)
    }
    f('zy','18')
    
    // 4.有1个参数,1个返回值
    # 第一个item是参数  第二个item是返回值
    var f = item => item
    var res = f('zy')
    console.log(res)
</script>

image

案例

类似搜索引擎,输入a就把a相关的内容显示出来,输入ab就把ab相关的内容显示出来

<body>
<div id="app">
    <h1>过滤案例</h1>
    <input type="text" placeholder="请输入要搜索的内容" v-model="myText" @input="handleInput">
    <ul>
        <li v-for="item in newDataList">{{item}}</li>
    </ul>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            myText: '',
            dataList: ['a', 'abc', 'aaa', 'jdhei', 'hslaa', 'sjoqjd', 'sqqsnz', 'qwjwo'],
            newDataList: ['a', 'abc', 'aaa', 'jdhei', 'hslaa', 'sjoqjd', 'sqqsnz', 'qwjwo'],
        },
        methods: {
            handleInput() {
                var _this = this
                this.newDataList = this.dataList.filter(function (item) {
                    //判断item在不在myText中(在会返回索引值,不在就返回-1)
                    // if (item.indexOf(_this.myText) >= 0) {
                    //     return true
                    // } else {
                    //     return false
                    // }
                    //表达式简写:
                    return item.indexOf(_this.myText) >= 0
                })
            }
        }
    })
</script>

但是以上methods代码中还可以再优化:箭头函数

<body>
<div id="app">
    <h1>过滤案例</h1>
    <input type="text" placeholder="请输入要搜索的内容" v-model="myText" @input="handleInput">
    <ul>
        <li v-for="item in newDataList">{{item}}</li>
    </ul>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            myText: '',
            dataList: ['a', 'abc', 'aaa', 'jdhei', 'hslaa', 'sjoqjd', 'sqqsnz', 'qwjwo'],
            newDataList: ['a', 'abc', 'aaa', 'jdhei', 'hslaa', 'sjoqjd', 'sqqsnz', 'qwjwo'],
        },
        methods: {
            handleInput() {
                // 使用箭头函数写法
                // this.newDataList = this.dataList.filter((item) => {
                //     return item.indexOf(this.myText) >= 0
                
                // 箭头函数继续优化:一个参数一个返回值
                this.newDataList = this.dataList.filter( item => item.indexOf(this.myText) >= 0)
            }
        }
    })
</script>

image

2)事件修饰符(了解)

用来给事件修饰的@click.stop

事件修饰符 释义
.stop 只处理自己的事件,阻止自己的事件冒泡给父标签(给子标签用)
.self 只处理自己的事件,子标签冒泡来的事件不处理(给父标签用)
.prevent 可阻止a标签链接跳转
.once 事件只会触发一次(适用于抽奖页面)
事件冒泡

当父标签里有一个子标签时,两个标签都加了事件,如果点击子标签会触发子标签的事件,同时还会连带触发父标签上的事件

<body>
<div id="app">
      <ul @click="handleUl">父标签
             <li @click="handleLi">子标签</li>
      </ul>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {},
      methods:{
          handleLi(){
            console.log('点击了li')
          },
          handleUl(){
            console.log('点击了ul')
          }
      }
    })
</script>

image

(1)stop[给子标签用]
<ul @click="handleUl">父标签
      <li @click.stop="handleLi">子标签</li>
</ul>

上述代码中给子标签的点击事件后加stop即可阻止事件冒泡:

点击子标签时触发事件不会再连带触发父标签的事件

image

(2)self[给父标签用]
<ul @click.self="handleUl">父标签
      <li @click="handleLi">子标签</li>
</ul>

上述代码中给父标签的点击事件后加self即可不接收子标签的事件冒泡:

image

(3)prevent

当给某个a标签后添加.prevent即可限制点击后不去跳转对应页面

补充:点击事件中如果加了location.href即可强制跳转某页面

<body>
<div id="app">
      <a href="https://www.baidu.com" @click.prevent="handleA">点击进入百度</a>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            handleA() {
                console.log('点击了a标签')
                //可以在这里进行判断:张三可以跳转 李四不可以跳转
                //location.href强制跳转某页面
                location.href='http://www.cnblogs.com'
            }
        }
    })
</script>

image

(4)once

当给事件后加.once后就会限制该事件只会触发一次(不刷新页面的情况下)

<body>
<div id="app">
    <h1>once 只响应一次</h1>
    <button @click.once="handleB">点击抽奖</button>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            handleB() {
                console.log('张三点击了抽奖')
            }
        }
    })
</script>

image

3)按键修饰符

按键事件

按了键盘某个键,就会触发函数的执行

按键事件 释义
@keydown 按下就触发
@keypress 按压就触发
@keyup 按下弹起时触发【常用】
<body>
<div id="app">
  <input type="text" placeholder="请按下键盘" v-model="text" @keyup="handleKeyUp">
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
          text:''
        },
        methods: {
            handleKeyUp(event) {
                console.log('按下了键盘:',event.key,'keyCode:',event.keyCode)
            }
        }
    })
</script>

image

常用的就回车(13)、删除(8)、ESC(27)

举例:当用户按下回车就登录、当用户按下ESC就关闭...

修饰符

根据上面的keyCode中的回车(13),我们可以直接操作当用户按下回车后执行函数(比上面简单)

<body>
<div id="app">
  <input type="text" placeholder="请按下键盘" v-model="text" @keyup.enter="handleKeyUp">
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
          text:''
        },
        methods: {
            handleKeyUp(event) {
                console.log('按下了回车')
            }
        }
    })
</script>

image

6.表单控制

也就是input框:
text :类型
radio:单选
checkbox:单选和多选

1)checkbox单选

checkbox 单选是选中或不选中 选中是true,不选中是false

<body>
<div id="app">
        <h1>checkbox单选</h1>
        <br>
        <input type="text" placeholder="请输入用户名" v-model="username">
        <br>
        <input type="password" placeholder="请输入密码" v-model="password">
        <br>
        记住密码:<input type="checkbox" v-model="remember">
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            username:'',
            password:'',
            remember:false,
        },
    })
</script>
</body>

image

2)单选radio

radio单选,多个radio绑定同一个变量,选中某个就对应value值

<body>
<div id="app">
    <h1>radio</h1>
            <input type="text" placeholder="请输入用户名" v-model="username">
            <br>
            <input type="password" placeholder="请输入密码" v-model="password">
            <br>
            <input type="radio" v-model="gender" value="1">男
            <input type="radio" v-model="gender" value="2">女
            <input type="radio" v-model="gender" value="0">未知
    <hr>
    您选择的性别是:{{gender}}
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            username:'',
            password:'',
            gender:'',
        },
    })
</script>
</body>

image

3)checkbox多选

checkbox 多选是选了好几个,定义一个数组,把选中不同的value放在数组中

<body>
<div id="app">
        <h1>checkbox多选</h1>
        <br>
        爱好:
        <input type="checkbox" v-model="check_many" value='化妆'>化妆
        <input type="checkbox" v-model="check_many" value='逛街'>逛街
        <input type="checkbox" v-model="check_many" value='吃饭'>吃饭
        <input type="checkbox" v-model="check_many" value='旅游'>旅游
        <hr>
        {{check_many}}
        <hr>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            check_many:[],
        },
    })
</script>
</body>

image

4)购物车案例

js的变量只要发生变化,html页面中使用该变量的地方就会重新渲染(刷新把变化后的数据放到页面)

(1)基本购物车

做到当选择商品打勾后下面的【选择的商品】和【消费的金额】动态变化

<body>
<div class="app">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <h1>购物车案例</h1>
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>商品编号</th>
                        <th>商品名字</th>
                        <th>商品价格</th>
                        <th>商品数量</th>
                        <th>全选/取消</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="item in goodList">
                        <th scope="row">{{item.id}}</th>
                        <td>{{item.name}}</td>
                        <td>{{item.price}}</td>
                        <td>{{item.number}}</td>
                        <td><input type="checkbox" v-model="checkGroup" :value="item"></td>
                    </tr>
                    </tbody>
                </table>
                <hr>
                <p>选择的商品:{{checkGroup}}</p>
                <!--放函数计算选中商品的价格-->
                <p>消费总金额:{{getPrice()}}</p>
            </div>
        </div>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            goodList: [
                {id: '1', name: '西游记', price: 10, number: 1},
                {id: '2', name: '红楼梦', price: 20, number: 1},
                {id: '3', name: '水浒传', price: 30, number: 1},
            ],
            checkGroup: [],
        },
        methods: {
            getPrice() {
                //通过选择的商品checkGroup 计算出里面的总价格
                var num = 0
                for (item of this.checkGroup) {
                    num += item.price * item.number
                }
                return num
            }
        },
    })
</script>

image

(2)功能:带全选或取消

要求:加一个全选和取消单选框,当全选时商品框都选中,取消时都取消。(注:当全选时下面某个商品取消单选框后上面的全选/取消还是对勾,需动态变化)

<body>
<div class="app">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <h1>购物车案例</h1>
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>商品编号</th>
                        <th>商品名字</th>
                        <th>商品价格</th>
                        <th>商品数量</th>
                        <th>全选/取消 <input type="checkbox" v-model="checkAll" @change="handlerCheckAll"></th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="item in goodList">
                        <th scope="row">{{item.id}}</th>
                        <td>{{item.name}}</td>
                        <td>{{item.price}}</td>
                        <td>{{item.number}}</td>
                        <td><input type="checkbox" v-model="checkGroup" :value="item" @change="handlerCheckOne"></td>
                    </tr>
                    </tbody>
                </table>
                <hr>
                <p>选择的商品:{{checkGroup}}</p>
                <p>是否全选中:{{checkAll}}</p>
                <!--放函数计算选中商品的价格-->
                <p>消费总金额:{{getPrice()}}</p>
            </div>
        </div>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            goodList: [
                {id: '1', name: '西游记', price: 10, number: 1},
                {id: '2', name: '红楼梦', price: 20, number: 1},
                {id: '3', name: '水浒传', price: 30, number: 1},
            ],
            checkGroup: [],
            checkAll: false
        },
        methods: {
            getPrice() {
                //通过选择的商品checkGroup 计算出里面的总价格
                var num = 0
                for (item of this.checkGroup) {
                    num += item.price * item.number
                }
                return num
            },
            handlerCheckAll() {
                //判断全选/取消标签是全选还是全不选
                if (this.checkAll) {
                    //如果是true(全选)就让选择的商品=商品列表
                    this.checkGroup = this.goodList
                } else {
                    //否则为空
                    this.checkGroup = []
                }
            },
            handlerCheckOne() {
                //判断选择的商品长度如果等于商品列表,就让全选/取消标签=true
                if (this.checkGroup.length == this.goodList.length) {
                    this.checkAll = true
                } else {
                    this.checkAll = false
                }
            },
        },
    })
</script>

image

(3)功能:带加减
<body>
<div class="app">
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <h1>购物车案例</h1>
                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>商品编号</th>
                        <th>商品名字</th>
                        <th>商品价格</th>
                        <th> 商品数量</th>
                        <th>全选/取消 <input type="checkbox" v-model="checkAll" @change="handlerCheckAll"></th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="item in goodList">
                        <th scope="row">{{item.id}}</th>
                        <td>{{item.name}}</td>
                        <td>{{item.price}}</td>
                        <td>
                            <!--给加减按钮添加函数判断是否大于1,是否没超过库存数,还要把item当作参数传进去-->
                            <button @click="handlerDown(item)">-</button>
                            {{item.number}}
                            <button @click="handlerUp(item)">+</button>
                        </td>
                        <td><input type="checkbox" v-model="checkGroup" :value="item" @change="handlerCheckOne"></td>
                    </tr>
                    </tbody>
                </table>
                <hr>
                <p>选择的商品:{{checkGroup}}</p>
                <p>是否全选中:{{checkAll}}</p>
                <!--放函数计算选中商品的价格-->
                <p>消费总金额:{{getPrice()}}</p>
            </div>
        </div>
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            goodList: [
                {id: '1', name: '西游记', price: 10, number: 1},
                {id: '2', name: '红楼梦', price: 20, number: 1},
                {id: '3', name: '水浒传', price: 30, number: 1},
            ],
            checkGroup: [],
            checkAll: false
        },
        methods: {
            getPrice() {
                //通过选择的商品checkGroup 计算出里面的总价格
                var num = 0
                for (item of this.checkGroup) {
                    num += item.price * item.number
                }
                return num
            },
            handlerCheckAll() {
                //判断全选/取消标签是全选还是全不选
                if (this.checkAll) {
                    //如果是true(全选)就让选择的商品=商品列表
                    this.checkGroup = this.goodList
                } else {
                    //否则为空
                    this.checkGroup = []
                }
            },
            handlerCheckOne() {
                //判断选择的商品长度如果等于商品列表,就让全选/取消标签=true
                if (this.checkGroup.length == this.goodList.length) {
                    this.checkAll = true
                } else {
                    this.checkAll = false
                }
            },
            handlerDown(item) {
                //当减到大于1时就不让减了(因为再减就是0应该给个删除按钮)
                if (item.number > 1) {
                    item.number--
                } else {
                    alert('剩一个了再减你还不如把我删了!T^T')
                }
            },
            handlerUp(item) {
                //假设库存为10,最大不能超过10
                if (item.number < 10) {
                    item.number++
                } else {
                    alert('我库存一滴都没有了 别加啦!>_<')
                }
            }
        },
    })
</script>

image

(5)补充:可变类型与不可变类型

Python中:

不可变类型:数字字符串元组
可变类型:列表字典集合,(对象也是,但是一般人无法理解)

当不可变类型当参数传入函数中时,修改这个参数外部的值不受影响
当可变类型当参数传入函数中时,修改这个参数外部的值会受影响!

但是其他语言中没有可变不可变类型,只有值类型引用类型。(python一切皆对象,所有东西都是类实例化出来的,数字字符串等都可以点出方法,所以对象本质上都是地址去改,其实也算引用。作者给规定的哪些是可变类型哪些是不可变类型)

提问:python函数参数传递是值传递还是引用传递?

答:问题不对,在python中没有值类型和引用类型,只有可变类型与不可变类型

上述案例中,js传入了item对象,在函数中修改会影响了原来的值。如果传数字不会影响原来的值。
所以js的对象是引用类型

(6)面试:深拷贝浅拷贝

7.v-model进阶

v-model只给input框用,因为只有input需要改值并实时绑定改变

修饰符 当失去焦点后
lazy 输入内容后当失去焦点后才获取
number 数字开头只保留数字,中间有字母的话字母到最后都不保留;
字母开头的都保留
trim 去除首位的空格

lazy好用的原因是:当用户输入完再校验,不用每输入一次就校验一次

<body>
<div class="app">
        <h1>lazy</h1>
        <input type="text" v-model.lazy="myText">---->{{myText}}
        <h1>number</h1>
        <input type="text" v-model.number="myNumber">---->{{myNumber}}
        <h1>trim</h1>
        <input type="text" v-model.trim="myTrim">---->{{myTrim}}
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            myText: '',
            myNumber: '',
            myTrim: ''
        },
    })
</script>

image

三.Vue与后端交互

跨域问题

只要向不是地址栏(域)的地址和端口发送请求拿数据,浏览器就会给拦截不让拿
前后端分离的项目就会引发跨域问题!

解决跨域问题

后端代码中在响应头中加入允许就可以

headers['Access-Control-Allow-Origin'] = '*'具体使用见下方详细代码中的使用

image

当前端点击一个按钮后去后端拿回用户信息并加载在页面上:

1.jquery发送ajax请求

#【django中view.py】django中FBV基于函数的视图写法

from django.http import JsonResponse

def index(request):
    user_dict = {'name': 'zy', 'age': 18}
    res = JsonResponse(user_dict)
    # 把这个key和value加入到响应头,就没有跨域问题了
    res.headers['Access-Control-Allow-Origin'] = '*'
    return res
#【html】中

<body>
<div class="app">
    <button @click="handleClick">点这里向后端发送请求获取用户信息</button>
    <div v-if="age!=0">
        <p>用户名:{{ name }}</p>
        <p>年龄:{{ age }}</p>
    </div>
    <div v-else>
        无信息
    </div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            name: '',
            age: 0,
        },
        methods: {
            handleClick() {
                $.ajax({
                    url: 'http://127.0.0.1:8000/index/',
                    type: 'get',
                    success: data => {
                        console.log(data)
                        this.name = data.name
                        this.age = data.age
                    }
                })
            },
        },
    })
</script>

2.fetch发送ajax请求(了解)

# fetch 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应
	-新的发送ajax接口
    -用起来比较方便
    -支持promise写法[最新的异步写法]
    -解决了原生的XMLHttpRequest兼容性的问题
    -不是所有浏览器都支持
    -主流现在是用【axios】第三方发送请求
# XMLHttpRequest: 原生js提供的
	-比较老,不同浏览器需要做一些兼容性的处理,写起来比较麻烦
    -jq基于它做了封装
<script>
        handleClick2() { 
                fetch('http://127.0.0.1:8000/index/').then(res => res.json()).then(res => {
                    console.log(res)
                    this.name = res.name
                    this.age = res.age
                })
            },
</script>

3.axios发送ajax请求(常用)

以后在vue上都用axios发送ajax请求,它是第三方的模块

Axios 是一个基于 promise 的 HTTP 库,也是基于XMLHttpRequest封装的

axios官网:http://www.axios-js.com/zh-cn/docs/

cdn:<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>

#【前端】完整代码在1.中,下列仅部分代码

<script>
        handleClick3() {
              //执行GET请求 执行POST请求就.post      res就是返回的结果,也是对象【后端一定要json格式】
                axios.get('http://127.0.0.1:8000/index/').then(res => {
                    // 后端真正的数据在res.data中(body体中的数据)
                    console.log(res.data)  
                    this.name = res.data.name
                    this.age = res.data.age
                })
            },
</script>
# 【后端django中的view】FBV基于函数的视图写法

from django.http import JsonResponse

def index(request):
    user_dict = {'name': 'zy', 'age': 18}
    res = JsonResponse(user_dict)
    # 把这个key和value加入到响应头,就没有跨域问题了
    res.headers['Access-Control-Allow-Origin'] = '*'
    return res

image

4.案例:点击按钮显示热门电影信息

https://m.maizuo.com/v5/?co=mzmovie#/films/nowPlaying

上面Vue与后端交互我们用了FBV基于函数的视图去写,这次试试CBV基于类的视图去写

由于用的drf继承APIView 不要忘记在settings.py中注册drf

发现问题:后端路径如果用相对路径会报错,改为绝对路径即可

后端返回json格式数据

# 【后端django中的view】CBV基于类的视图写法 drf

from rest_framework.views import APIView
import json
from rest_framework.response import Response

class ASS(APIView):
    def get(self, request):
        # 不用绝对路径可能会出错
        with open(r'D:\pythonProject1\vue_day04_django\info.json', 'r', encoding='utf-8') as f:
            res_dict = json.load(f)
            res = Response(res_dict)
            # 把解决跨域问题的代码加入到响应头中
            res.headers['Access-Control-Allow-Origin'] = '*'
            return res

前端:请求回来拿到数据,赋值给dataList 页面就渲染了

#【前端】不要忘记导axios的cdn

<body>
<div class="app">
    <h1>点击按钮显示热门电影信息</h1>
    <button @click="handlerClick">点击</button>
    <ul>
        <li v-for="item in dataList">
            <h2>名字:{{item.name}}</h2>
            <h3>导演:{{item.director}}</h3>
            <h3>类型:{{item.category}}</h3>
            <p>简介:{{item.synopsis}}</p>
            <img :src="item.poster" alt="" height="300px" width="200px">
            <hr>
        </li>
    </ul>
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            dataList: []
        },
        methods: {
            handlerClick() {
                 //执行GET请求.get  执行POST请求.post      res就是对象
                axios.get('http://127.0.0.1:8000/index1/').then(res => {
                    // 后端真正的数据在res.data中(body体中的数据)
                    console.log(res.data.data.films)
                    this.dataList=res.data.data.films
                })
            },
        }
    })
</script>

image

四.Vue生命周期钩子

vue实例创建开始实例被销毁,总共经历了8个生命周期钩子只要写了就会执行函数

钩子:反序列化验证(钩子函数)
专门名字:面向切面编程(AOP)
OOP:面向对象编程

img

生命周期流程

new一个Vue实例时先执行beforeCreate钩子, 当Vue实例创建出来后执行created钩子,然后挂载在这个实例上一些模板(就是el:'#app' 管理的那个div标签),这个时候data上就有值了(data定义变量是1就是1了),此时el还没挂载上还要执行beforeMount挂载之前的钩子,挂载完执行mounted钩子。这个时候页面上就可以看到各种样式,这个时候就停在这里了。
当想改变js的变量就会去执行beforeUpdate,更改完执行updated,然后一直重复在这里。直到我要把这个实例销毁掉。当我想销毁实例就先执行beforeDestroy,销毁完执行destroyed

8个生命周期钩子函数 描述
beforeCreate 创建Vue实例之前调用data,el都没有
created 创建Vue实例成功后调用(可以在此处发送异步请求后端数据)
data有了,el没有的
beforeMount 渲染DOM之前调用data有了,el没有
mounted 渲染DOM之后调用
beforeUpdate 重新渲染之前调用(数据更新等操作时,控制DOM重新渲染)
updated 重新渲染完成之后调用
beforeDestroy 销毁之前调用
destroyed 销毁之后调用

以上重点:

1.用的最多的是created ,一般在这里向后端发送ajax请求加载数据(有的人会在mounted中加载)

案例:打开页面直接显示热门电影信息

以下为部分代码,不点击按钮 直接发送ajax请求

<script>
    var vm = new Vue({
        el: '.app',
        data: {
            dataList: []
        },
        methods: {},
        // 创建Vue实例成功后调用(可以在此处发送异步请求后端数据)
        created(){
            axios.get('http://127.0.0.1:8000/index1/').then(res=>{
                this.dataList=res.data.data.films
            })
        }
    })
</script>

2.beforeDestroy常用在两个人实时聊天中
-组件一创建,可以在created中启动一个定时器任务
-当组件被销毁,可以在beforeDestroy中放取消定时器任务

1.n毫秒后执行代码:
#几毫秒后执行一次代码
let t = setTimeout(参数1,参数2)
//参数1:可以放js语句,或函数名
//参数2:放毫秒
#几毫秒后循环执行代码
let s = setInterval(参数1,参数2)
//参数1:可以放js语句,或函数名
//参数2:放毫秒

2.取消定时任务
#取消执行一次的
clearTimeout(t)
#取消循环执行的
clearInterval(s)

案例:实现实时聊天效果(在线聊天室)

实现聊天的发展史:http>>轮询>>长轮询>>websocket

-http:当张三给李四发消息时,张三的消息会存在服务端,由于http协议规定了服务端不能主动给客户端发消息,也就是李四如果不朝服务端发送请求就无法拿到张三的消息,于是就有了轮询(轮询也是基于http的)

-轮询:定时器+ajax 发过去请求然后带回来服务端的数据,回来后立马再发过去请求【重复发!】
-长轮询:定时器+ajax 隔一段时间发请求过去有就带回来,没有就再隔一段时间发

由于http是基于请求响应的有很大弊端,所以就有了websocket协议

-websocket协议:服务端主动推送消息(不是所有浏览器都兼容)【建议用】
https://zhuanlan.zhihu.com/p/371500343

五.vue组件

组件:类似Python中的模块 拓展HTML元素 封装可重复使用的代码 目的就是重复使用

工程化开发之后:1个组件 就是1个xx.vue

var vm = new Vue({})这个实例就是根组件

组件又分局部组件和全局组件:
全局组件在任意组件中都可以使用
局部组件只能在当前组件中使用

image

例如:有一个轮播图,可以在很多页面中使用,一个轮播有js,css,html
组件把js,css,html放到一起,有逻辑,有样式,有html

image

全局组件

在任意组件中都可以使用的组件

<body>
<div class="app">
    <!--使用全局组件-->
    <child></child>
    <hr>
    <!--使用全局组件-->
    <child></child>
</div>
</body>
<script>
    // 创建一个全局组件
    Vue.component('child', {
        // 里面写html内容,必须包在一个标签中
        template: `
          <div>
          <h1>全局组件</h1>
          <button @click="handleClick1">前进</button>
          <h2>{{ title }}</h2>
          <button @click="handleClick2">后退</button>
          </div>`,
        //data必须是方法,返回一个对象【原因是组件可以用多次 每个人返回一个就不会造成冲突】
        data: function () {
            return {
                title: '嘿嘿'
            }
        },
        methods: {
            handleClick1() {
                alert('前进')
            },
            handleClick2() {
                alert('后退')
            }
        }
    })
    var vm = new Vue({
        el: '.app',
        data: {},
    })
</script>

image

局部组件

只能在当前组件内部中使用的组件

<body>
<div class="app">
    <!--使用局部组件-->
    <foo></foo>
</div>
</body>
<script>
    // 给局部组件的内容起一个名字在下面根组件中创建
    var foo = {
        template: `
          <div>
          <h1>局部组件</h1>
          <h2>{{ title }}</h2>
          <button @click="handleClick1">前进</button>
          <button @click="handleClick2">后退</button>
          </div>`,
        data: function () {
            return {
                title: '嘿嘿'
            }
        },
        methods: {
            handleClick1() {
                alert('前进')
            },
            handleClick2() {
                alert('后退')
            }
        }
    }
    //用上面提前写好的局部组件内容
    var vm = new Vue({
        el: '.app',
        data: {},
        //创建局部组件
        components: {
            foo
        }
    })
</script>

image

2.组件中的其他知识

1)根组件 和 组件需明白知识点

  • var vm = new Vue({})这个实例就是根组件 它会管理一个div标签

  • 自己定义的全局、局部的组件就是普通组件

  • 每个组件都有自己的html、css、js、数据、事件等

  • 在组件中,this代指当前组件(this在跟组件中就是当前实例)

  • data是1个函数,需要有返回值(return)

  • 根组件(Vue管理的div标签)中无法拿到全局或局部组件中的数据,同理全局或局部组件也拿不到根组件中的数据

  • 父子组件的data是无法共享的

image

image

3.组件间通信

由于组件间data数据不共享,要想共享需要进行数据传递

1)父传子

如果一定要把父组件中的数据给子组件使用,那就需要自定义属性的方式自定义属性名不能用驼峰,也不能和子组件中的名字冲突

以下子组件用局部组件为例

<body>
<div class="app">
    <h1>根组件</h1>
    <p>根组件中的年龄是:{{age}}  局部组件中的姓名是:{{name}}</p>
    <hr>
    <h1>局部组件</h1>
    <h3>父传子需要用自定义属性的方式(同名的属性名)</h3>
    <child :age="age"></child>
</div>
</body>
<script>
    //组件中的内容
    var c = {
        //里面写html内容 且必须包在一个标签中
        template: `
            <div>
            子组件(局部组件):姓名:{{name}}
            父组件(根组件)中的年龄:{{age}}
            </div>`,
        //data必须是方法 且返回一个对象
        data: function () {
            return {
                name:'zy',}
        },
        //接收父传子的自定义属性(可传多个)
        props:['age']
    }
    var vm = new Vue({
        el: '.app',
        data: {
            age:18,},
        //创建一个局部组件(用上面提前写好的组件内容)
        components:{
            child:c},
    })
</script>

image

自定义属性时不同方式显示结果不同

<h4>:age="age"  结果是根组件中age的值</h4>
<child :age="age"></child>

<h4>age="age"  结果是字符串'age'</h4>
<child age="age"/></child>

<h4>:age="19"  结果是数字19</h4>
<child :age="19"/></child>

<h4>age="19"  结果是字符串'19'</h4>
<child age="19"/></child>

image

2)子传父

如果要把子组件中的数据给父组件使用,需要自定义事件的方式

举例:在子组件中的input标签上输入内容点击按钮显示到父组件中

以下子组件用局部组件为例

<body>
<div class="app">
    <h1>根组件</h1>
    <p>子组件中传来的数据:{{mytext}}</p>
    <hr>
    <h1>局部组件</h1>
    <!--子传父需要用自定义事件的方式-->
    <child @myevent="handleEvent"></child>
</div>
</body>
<script>
    //组件中的内容
    var c = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <input type="text" v-model="mytext">
          <button @click="handleSend">点击发送到根组件中</button>
          </div>`,
        //data必须是方法 且返回一个对象
        data: function () {
            return {
                mytext: '',
            }
        },
        methods: {
            handleSend() {
                //子组件中触发自定义事件的执行 ('自定义事件名',要传的参数)
                this.$emit('myevent', this.mytext)
            }
        },
    }
    var vm = new Vue({
        el: '.app',
        data: {
            //因为数据不共享 所以用同名也没问题
            mytext: ''
        },
        methods: {
            //自定义事件(接收的参数)
            handleEvent(mytext) {
                //父组件中的mytext=传过来的mytext
                this.mytext = mytext
            },
        },
        //创建一个局部组件(用上面提前写好的组件内容)
        components: {
            child: c
        },
    })
</script>

image

需明白:this.$emit('自定义事件名',要传的参数),可以去触发子组件标签上写在父组件里的自定义事件

3)ref属性实现父子通信

上面了解到自定义属性可以实现父传子自定义事件可以实现子传父

除了上面两种Vue还提供了一个ref属性,可以更方便的实现父子通信

(1)ref属性放在普通标签上
#  <input type="text" ref="myinput">
	-通过this.$refs.myinput  拿到的是'原生dom对象'
    -this.$refs.myinput.value = 'zy浦东第一深情'     通过原生dom可修改标签属性(不推荐)

一般不推荐使用,因为我们不会直接去操作dom

<body>
<div class="app">
    <button @click="handleClick">点击</button>
    <br>
    <input type="text" ref="myinput">
    <div ref="mydiv">我是div</div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            name:'zy'
        },
        methods: {
            handleClick(){
                //通过this.$refs就可以拿到所有标签上写了ref属性的标签,是对象类型
                //k是ref对应的v值,v是原生dom对象
                console.log(this.$refs)
                //我们就可以直接修改原生dom对象的value属性,input就有值了
                this.$refs.myinput.value = 'zy浦东第一深情'
            },
        },
    })
</script>

image

(2)ref属性放在组件上

实现父子通信【推荐使用】

# <child ref="mychild"></child>
	-通过this.$refs.mychild  拿到的是'组件对象',可以 .组件中的属性、方法(如果子组件中有方法也是可以点的)
    
    -当想子传父时:直接把父中age=子中age
          this.age=this.$refs.mychild.age
    -当想父传子时:直接把子中age=父中age
          this.$refs.mychild.age=this.age

以下组件用局部组件为例

<body>
<div class="app">
    <h1>父组件</h1>
    <h4>父组件中的age:{{age}}</h4>
    <button @click="handleClick">点击把子组件中的age放在父组件age中</button>
    <hr>
    <h1>子组件</h1>
    <child ref="mychild"></child>
</div>
</body>
<script>
    //组件中的内容
    var c = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h4>子组件中的年龄:{{age}}</h4>
          </div>`,
        //data必须是方法 且返回一个对象
        data: function () {
            return {
                age:18}},
    }
    var vm = new Vue({
        el: '.app',
        data: {
            age:999},
        methods: {
            handleClick(){
                //通过this.$refs就可以拿到所有标签上写了ref属性的标签,是对象类型
                //k是ref对应的v值,v是组件
                console.log(this.$refs)
                //当想子传父时:直接把父中age=子中age
                this.age=this.$refs.mychild.age
                //当想父传子时:直接把子中age=父中age
                //this.$refs.mychild.age=this.age
            },
        },
        //创建一个局部组件(用上面提前写好的组件内容)
        components: {
            child: c},
    })
</script>

image

4.动态组件

案例:做一个页面,当点击首页时显示首页内容,点击订单显示订单内容,点击商品显示商品内容

image

1)用最基础的v-if判断

<body>
<div class="app">
    <span @click="handleClick('c1')">首页</span><span @click="handleClick('c2')">博客</span><span @click="handleClick('c3')">后台</span>
    <c1 v-if="choice=='c1'"></c1>
    <c2 v-else-if="choice=='c2'"></c2>
    <c3 v-else></c3>
</div>
</body>
<script>
    //组件中的内容
    var c1 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>首页</h1>
          </div>`,
    }
    var c2 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>博客</h1>
          </div>`,
    }
    var c3 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>后台</h1>
          </div>`,
    }
    var vm = new Vue({
        el: '.app',
        data: {
            //默认显示的是c1页面
            choice:'c1'
        },
        methods: {
            //接收点击事件传过来的参数
            handleClick(i) {
                this.choice=i
            },
        },
        //创建一个局部组件(用上面提前写好的组件内容) 注册一下
        components: {
            c1, c2, c3
        },
    })
</script>

该方法太基础,且做了判断感觉有点呆,所以需要用到动态组件component标签去实现

2)动态组件component标签实现

    <!--动态绑定一个变量,is对应哪个字符串-->
    <component :is="who"></component>
<body>
<div class="app">
    <span @click="handleClick('c1')">【首页】</span><span @click="handleClick('c2')">【博客】</span><span @click="handleClick('c3')">【后台】</span>
    <!--动态绑定一个变量-->
    <component :is="who"></component>
</div>
</body>
<script>
    //组件中的内容
    var c1 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>首页</h1>
          </div>`,
    }
    var c2 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>博客</h1>
          </div>`,
    }
    var c3 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>后台</h1>
          </div>`,
    }
    var vm = new Vue({
        el: '.app',
        data: {
            who:'c1'
        },
        methods: {
            //接收点击事件传过来的参数
            handleClick(i) {
                //点击谁 谁就是who
                this.who=i
            },
        },
        //创建一个局部组件(用上面提前写好的组件内容) 注册一下
        components: {
            c1, c2, c3
        },
    })
</script>

发现component标签实现更简单

2)keep-alive属性,保证组件不被销毁

比如某个组件上有个搜索框 在里面输入内容后切到下一个组件中然后再切回来,输入框中的内容会被清空。要想让里面内容一直存着 就需要用到keep-alive属性

<body>
<div class="app">
    <span @click="handleClick('c1')">【首页】</span><span @click="handleClick('c2')">【博客】</span><span
        @click="handleClick('c3')">【后台】</span>
    <!--动态绑定一个变量-->
    <keep-alive>
        <component :is="who"></component>
    </keep-alive>

</div>
</body>
<script>
    //组件中的内容
    var c1 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>首页</h1>
          </div>`,
    }
    var c2 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>博客</h1>
          <input type="text" placeholder="输入要搜索的内容"><button>搜索</button>
          </div>`,
    }
    var c3 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>后台</h1>
          </div>`,
    }
    var vm = new Vue({
        el: '.app',
        data: {
            who: 'c1'
        },
        methods: {
            //接收点击事件传过来的参数
            handleClick(i) {
                //点击谁 谁就是who
                this.who = i
            },
        },
        //创建一个局部组件(用上面提前写好的组件内容) 注册一下
        components: {
            c1, c2, c3
        },
    })
</script>

image

5.slot插槽

一般编写完一个组件之后组件的内容都是写死的,需要加数据只能去组件中修改扩展性太差!所以就出现了插槽,只需在组件中添加<slot></slot>,就可以在body的组件标签中添加内容,然后就可以替换到slot标签中

1)匿名插槽

<body>
<div class="app">
    <!--②在子组件中直接写内容就可以替换到插槽中-->
    <c1>
        <img src="./img.png" alt="">
    </c1>
</div>
</body>
<script>
    //组件中的内容
    var c1 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>①下面放入插槽 以后在body体中填充内容</h1>
            <hr>
                <slot></slot>
            <hr>
          </div>`,
    }
    var vm = new Vue({
        el: '.app',
        data: {},
        methods: {},
        //创建一个局部组件(用上面提前写好的组件内容) 注册一下
        components: {
            c1
        },
    })
</script>

image

以上是匿名插槽,假如我组件中有多个插槽想给不同插槽添加不同的图片,这就需要用到具名插槽

2)具名插槽

<body>
<div class="app">
    <c1>
        <div slot="a">
            <img src="./img.png" alt="">
        </div>
        <div slot="b">
            <img src="./img_2.png" alt="">
        </div>
    </c1>
</div>
</body>
<script>
    //组件中的内容
    var c1 = {
        //里面写html内容 且必须包在一个标签中
        template: `
          <div>
          <h1>下面放入插槽 以后在body体中填充内容</h1>
          <h3>给不同插槽添加一个名字</h3>
            <hr>
                <slot name="a"></slot>
            <hr>
                <slot name="b"></slot>
            <hr>
          </div>`,
    }
    var vm = new Vue({
        el: '.app',
        data: {},
        methods: {},
        //创建一个局部组件(用上面提前写好的组件内容) 注册一下
        components: {
            c1
        },
    })
</script>

image

6.计算属性

  • 计算属性只有使用的变量发生变化时才重新运算

  • 计算属性就像Python中的@property,可以把方法伪装成属性(变成属性就可以用v-fo循环了!)

1)计算属性基本使用

image

发现当页面刷新函数就会重新计算,有点消耗资源!我们想实现只有和它关联的刷新它再重新计算,这个时候就要用到计算属性了

配置项:computed:{},里面写的方法可以当作属性使用,里面返回什么结果就是什么

<body>
<div class="app">
    <h4>输入单词,首字母会转成大写并展示到右侧</h4>
    <input type="text" placeholder="输入内容" v-model="mytext">---->{{newText}}
    <h4>发现只要页面刷新上面伪装成属性的函数(计算属性)不会再重新计算</h4>
    <input type="text" placeholder="输入内容" v-model="age">
</div>
</body>
<script>
    var vm = new Vue({
        el: '.app',
        data: {
            mytext: '',
            age:''
        },
        methods: {},
        //计算属性配置项
        computed:{
            newText(){
                //【.slice(0,1).toUpperCase()】对字符串0~1的位置进行切割并转大写
                console.log('我执行了一次')
                return this.mytext.slice(0, 1).toUpperCase() + this.mytext.slice(1)
            },
        },
    })
</script>

image

2)用计算属性重写前面的过滤案例

newDataList做成计算属性v-for直接循环它

newDataList使用的变量只要发生变化它就会重新运算,使用了mytext只要input输入内容,它就会重新运算不需要再加事件了

<body>
<div id="app">
    <h1>过滤案例</h1>
    <input type="text" placeholder="请输入要搜索的内容" v-model="myText">
    <ul>
        <!--计算属性会把方法变成属性 所以可以v-fo循环-->
        <li v-for="item in newDataList">{{item}}</li>
    </ul>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            myText: '',
            dataList: ['a', 'abc', 'aaa', 'jdhei', 'hslaa', 'sjoqjd', 'sqqsnz', 'qwjwo'],
        },
        //计算属性配置项
        computed: {
            newDataList() {
                return this.dataList.filter(
                    item => item.indexOf(this.myText) >= 0)
            },
        },
    })
</script>

7.监听属性

data中定义的属性,可以使用watch绑定一个方法 只要这个属性变化了就会触发函数的执行

以下为伪代码,重点了解watch绑定的方法发生改变就会触发一个函数的执行

<body>
<div id="app">
    <span @click="course_type=1">【Python】</span><span @click="course_type=2">【Linux】</span>
    <div>假设这里有很多课程,点击上面的标签就完成对应课程的过滤</div>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            //默认为0查所有
            course_type:'0'
        },
        //生命周期钩子函数:向后端发送ajax请求拿数据
        created(){
            this.getData()
        },
        methods:{
            getData(){
                //发送ajax,通过course_type过滤获取课程
                //http://127.0.0.1:8080/api/v1/courses?course_type=0
            },
        },
        //监听属性
        watch:{
            course_type(){
                console.log('数据变动后就会执行[向后端发送ajax请求拿数据]的函数')
                this.getData()
            },
        },
    })
</script>

image

共用过的配置项

	- el(template)
    - data :组件中是方法
    - methods:写事件绑定的方法、公共的方法
    - 8个生命周期钩子
    - components:配置局部组件的
    - watch:监听属性
    - computed:计算属性

六.Vue-cli项目搭建

Vue-CLI也叫vue的脚手架,可以创建vue项目

使用vue的脚手架必须安装node js(解释型语言)

1.node js是一门'后端语言'
2.JavaScript只能运行在浏览器中,因为浏览器中有他的解释器环境
3.大佬们基于谷歌浏览器的v8引擎,使它能够运行在操作系统上(这就成了js的解释器)里面拥有:
    	-文件操作的模块
        -网络操作的模块
        -数据库操作的模块
      这就组成了node js
   前端人员就可以不用再学后端语言 直接用js在node js中写后端(包括操作数据库)就可以
4.node js号称性能高,因为大量使用了协程

1.node js下载与安装

http://nodejs.cn/ 下载对应平台的nodejs解释器

# 安装完成会有两个可执行文件,可对照python中的文件记忆
    python    node 
    pip           npm(npm太慢 后面要换成cnpm)

# 打开cmd
    node -v        可查看版本信息
    node            进入到了node环境
    npm install   装模块
    .exit              退出

image

补充:解释型语言和编译型语言

1.js、node、php、python    都是'解释型语言'运行解释器之上   
    python可以用pyinstaller打包成可执行文件

2.c、go、c++                       都是'编译型语言'直接把源代码编译成不同平台的可执行文件
	cpython解释器是用c写的:它编译成不同平台的可执行文件,在不同平台【双击就可运行】,如win、mac、linux

3.java                                    是一处编码处处运行,最终定型是'编译型'
	-java 虚拟机:虚拟机跨平台
    -把java字节码文件运行在虚拟机之上
    -java写了代码在任意平台都能编写字节码(jar、war、.class)'区别于可执行文件',只要其他平台有java虚拟机就可以运行

2.Vue-cli创建项目

前端做成项目:使用vue-cli工具创建出vue项目,以后就是单页面应用(spa)组件化开发,由于 xx.vue、ts、saas、less在浏览器中无法识别,所以需要进行编译变成纯碎的html、js、css(浏览器只能识别这三种)使它们可以在浏览器中执行。

vue-cli不仅可以创建出vue项目进行开发,上线时还可以把一些语法编译成浏览器可以识别的三种语法

1)创建vue项目使用什么?

官网:https://cli.vuejs.org/zh/

vue2.x 使用vue-cli
vue3.x 使用vue-cli、也可以用vite(效率非常高)

2)安装vue-cli

①.由于它是在外网下载的速度会很慢,建议用先下载一个cnpm,用cnpm替换npm
pycharm中命令行安装:npm install -g cnpm --registry=https://registry.npm.taobao.org
-g 表示装全局
--registry=https://registry.npm.taobao.org 表示指定淘宝镜像

安装不了就重启一下pycharm试试,或去cmd中试

image

②.这样我们的cnpm就下载好了,然后用cnpm去下载vue-cli
​ pycharm中命令行安装:cnpm install -g @vue/cli

安装好后就可以用cmd控制台输入Vue命令(类似装了djagno可以使用django-admin创建django项目),同理使用Vue就可以创建Vue项目

3)使用Vue-cli创建项目

注:需要先找个找个目录然后再执行创建项目

cmd中①先D:去D盘根目录,②输入vue create myfirstvue创建项目

image

image

image

image

image

image

image

有的话就可以用pycharm打开该项目文件了

image


创建速度慢可以ctrl+c停止后执行npm cache clean --force清理缓存重新创建

# 很慢的原因
①是从github拉一个空项目
②安装该项目所有的依赖,在用很慢的npm装!

3.Vue项目目录介绍

# pychrm 打开项目后,运行项目有两种方式
	1.在pycharm>>Terminal命令行中执行(注意路径:D:\myfirstvue>)
    	npm run serve   运行项目
        ctrl+C                可停掉项目
        
    2.使用 pycharm添加npm后,点击绿色箭头运行即可执行
————————————————
  #  以上两种方式都可

image

image

# vue 项目目录介绍
myfirstvue      # 项目名
	+node_modules  # 非常多第三方模块、依赖,以后【把项目复制给别人时该文件夹要删掉(上传git要忽略掉)】,项目要运行没有它不行。如果没有只需要在【项目目录中执行 cnpm install】,根据package.json的依赖包安装好依赖
    
    +public              # 文件夹--以后都不要动这里面的东西
    	-favicon.ico    # 网站小图标
        -index.html     # spa 单页面应用,以后整个vue项目都是用这一个html动态
        
    +src                  # 文件夹--以后我们都动这里面的代码
        -assets          # 静态资源,以后前端用的图片、js、css..都放在这里
    		logo.png       #  图片
        -components        # 【小组件】以后在这里放小组件 xx.vue,都是给页面组件用的**
			HelloWorld.vue  # 提供的展示组件,可以删掉
        -router               # 安装了Router就会有这个文件夹,下面有个index.js
            index.js
        -store               # 安装了Vuex就会有这个文件夹,下面有个index.js
            -index.js
        -views               # 【页面组件】根组件包着页面组件,页面组件里放小组件**
            -AboutView.vue
            -HomeView.vue
        -App.vue            # 【根组件】,new Vue实例管理了 div,原来写在div中的东西现在都写在App.vue
        -main.js            # 项目的启动入口

	.gitignore             # git的忽略文件,后面会讲
    babel.config.js        # bable配置文件,不用动
    jsconfig.json          # 配置文件,不用动
    package.json           # 安装了第三方模块,它自动增加,不用动
    package-lock.json      # 锁定文件,不用管
    README.md              #  用户手册
    vue.config.js          # vue的配置文件

4.es6导入导出语法

1)先了解App.vue、main.js、About.vue写了什么

#【xx.vue】研究学到的重点:

 '注:以后开发vue项目,都按照这个模式来'
1.新建xx.vue
2.在xx.vue组件中写三块内容
	# 以后组件的【html内容】都写在这里(且必须在一个标签中写,如:div)
	<template>
	</template>
    
    # 以后该组件使用的【样式】都写在这
    <style>
    </style>
    
    # 以后【js】的东西都写在这
    <script>
	</script>
    
——————————————————————————

#【 main.js】研究学到的重点:

# 以下的意思是找到【index.html】中的id为app的div,本来要在div里面写的内容都在【App.vue】中写
new Vue({
  ...
  render: h => h(App)
}).$mount('#app')

image

2)导入导出语法

python可以创建包,然后在其他py文件中导入使用
js从es6开始,也支持包的导入和导出

(1)默认导出导入语法
导出
//默认导出语法(用的最多)只能导出一个,一般导出一个对象

var name='帅哥'

function add(a,b){
    return a+b
}

//如果想在外部包中使用 就必须导出,如果没导出就是隐藏字段无法使用
//export default {name:name,add:add}
export default {name,add}   //可简写

image

导入
import 别名  from '路径'  // [别名] 就是导出的对象
——————————————————————————

//使用zybb包下的zy.js里的变量和函数
import zy from './zybb/zy'   //导入

//类似print() 只能在网页中的检查中看
console.log(zy.name)
console.log(zy.add(10,20))

image

(2)命名导出导入语法(了解)
导出
var name='帅哥'

function add(a,b){
    return a+b
}

// 导出   可以导出多个 【并起一个名字】
export const myname=name
export const myadd=add
导入
// 导入  {里面可选用一个或多个}
import {myname, myadd} from './zybb/zy'

console.log(myname)
console.log(myadd(10, 20))
导入的简写

如果包下有index.js 导入的时候直接导到包就可以,不用详细到index,一定要是index.js才可以

包下的【index.js】导入的时候 可以不用详细写到.../index.js的路径  --->它等同于ptyhon的__init__.py

比如:
        包是zy  里面有一个index.js
        #导入的时候:(aaa是给导入的对象起的别名)
        	import aaa from './zy'

3)vue项目编写步骤

# 1 以后只需要写xx.vue组件
	-页面组件
    -小组件:就是给页面组件用的

# 2 组件中导出
	export default {
          name: 'HelloWorld',
          data() {
            return {
              xx: '彭于晏'
            }
          },
	}

# 3 在别的组件中要用,导入、注册
	# 导入
	import HelloWorld from '../components/HelloWorld.vue'
    # 注册
    export default {
      components: {
        HelloWorld 
      }
    }
    
 # 4 注册以后,在这个组件中就可以使用导入的组件 ,写在<template>里
	# 自定义属性
	<HelloWorld msg="传进来的p"/>	
(1)创建项目后先修改两个页面

😶App.vue 该页面需要删的剩下这些

<template>
  <div id="app">
      
        <!--不用管-->
    	<router-view/>
  </div>
</template>

😶main.js 该页面不用删东西

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

Vue.config.productionTip = false

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')
(2)访问根路径
# 访问根路径就会显示【 HomeView.vue】页面组件组件页面
	-HomeView.vue('导入语法')使用了components>中的>Helloworld.vue小组件('导出语法')
    -自定义属性
    
# 访问 【/about】 会显示AboutView.vue页面组件

image

5.小练习-登录功能

1)在AboutView.vue中写登录功能

访问 【/about】 会显示AboutView.vue页面组件

2)使用axios给后端发送ajax请求

(1)安装

pycharm中命令行安装:cnpm install axios -S

(2)导入与使用

import axios from 'axios' 在需要用的地方直接导入

使用:

handleClick() {
      console.log(this.username, this.password)
      axios.post(
                  'http://127.0.0.1:8000/login/',
                  {username: this.username, password: this.password}).then(res => {
        // console.log(res)
        alert(res.data.msg)
      })
    }

3)解决跨域

1 安装模块
	pip3 install django-cors-headers -i https://pypi.tuna.tsinghua.edu.cn/simple/

2 注册app
	'corsheaders'
3 中间件中添加
	'corsheaders.middleware.CorsMiddleware',
4 配置文件添加
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
	'DELETE',
	'GET',
	'OPTIONS',
	'PATCH',
	'POST',
	'PUT',
	'VIEW',
)

CORS_ALLOW_HEADERS = (
	'XMLHttpRequest',
	'X_FILENAME',
	'accept-encoding',
	'authorization',
	'content-type',
	'dnt',
	'origin',
	'user-agent',
	'x-csrftoken',
	'x-requested-with',
	'Pragma',
	'token'
)

4)代码

vue项目的AboutView.vue中

<template>
  <div class="about">
    <h1>登录功能</h1>
    <p><input type="text" v-model="username"></p>
    <p><input type="password" v-model="password"></p>
    <button @click="handle">登录</button>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  name:'AboutView',
  data(){
    return {
      username:'',
      password:'',
    }
  },
  methods:{
    handle(){
      // console.log(this.username,this.password)
      axios.post(
          'http://127.0.0.1:8000/login/',
          //往请求体中传入数据发送给后端
          {username:this.username,password:this.password}).then(res=>{
        alert(res.data.msg)
      })
    },
  },
}
</script>

后端django中的view.py

from django.http import JsonResponse
import json

def login(request):
    # FBV前后端交互需要用json格式
    request.data=json.loads(request.body)
    username = request.data.get('username')
    password = request.data.get('password')
    if username == 'zy' and password == '123':
        return JsonResponse({'code': 100, 'msg': '登录成功', 'token': 'sdfsaf'})
    else:
        return JsonResponse({'code': 101, 'msg': '登录失败'})

6.scoped当前组件生效样式

# 父组件(App.vue)的样式在子组件中都会生效(包括页面组件和小组件),不同的页面组件样式互相不会生效。

# scoped可以让该样式只在当前组件中生效

<style scoped>
h1 {
  background-color: chartreuse;
}
</style>

七.Vue高级用法

纯净的vue项目

1.components中的所有小组件都删掉留一个空文件夹
2.views中的AboutView删掉留下HomeView并删掉一些不要的东西
        <template>
              <div class="home"></div>
        </template>
        <script>
        //导出页面组件
        export default {
              name: 'HomeView',
              data() {},}
        </script>
3.router>>index.js里AboutView的路由删掉
4.App.vue删掉一些不要的东西
        <template>
              <div id="app">
                <router-view/> //主要用来替换页面组件 一般不用管
              </div>
        </template>
5.main.js中删掉一些不要的东西
        import Vue from 'vue'
        import App from './App.vue'
        import router from './router'
        import store from './store'

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

今后在views>>HomeView写内容即可(页面组件)
在components下可以创建小组件写内容

1.props接收外部数据的三种用法

props 自定义属性,让组件接收外部传过来的数据

//方式一:使用数组 
props:['name']
    
//方式二:使用对象   因为限制接收数字如果传来的是字符串页面会显示但是控制台会报错
 props: {name: Number}
        
//方式三:使用对象,默认值和必填
  props: {
    name: {
      type: String, //类型
      required: true, //必填
      default: '老王' //默认值
    }
  }

代码:

页面组件【HomeView.vue】(父组件)中

<template>
  <div class="home">
    <h1>页面组件(父组件)</h1>
    <hr>
    <h2>小组件(子组件)</h2>
    <Hello></Hello>
    <hr>
  </div>
</template>

<script>
//导入子组件
import Hello from "@/components/Hello";
//导出页面组件
export default {
  name: 'HomeView',
  data(){
    return{
      name:'zy'
    }
  },
  //导入子组件后需注册
  components:{
    Hello
  }
}
</script>

小组件【Hello.vue】(子组件)中

<template>
  <div class="home">
    <button>后退</button>
    首页(父组件)传入的数据:{{name}}
    <button>前进</button>
  </div>
</template>

<script>
//导出
export default {
  name: "Hello",
  //接收父组件传来的数据
      
  //方式一:用数组接收
  props:['name']

  //方式二:用对象接收
  // props:{name:Number}

  //方式三:用对象接收 且带默认值和必填限制
  // props:{
  //   name:{
  //     type:String,
  //     required:true,
  //     default:'帅哥'
  //   }
  // }
}
</script>

image

2.mixin混入

可以把多个组件共用的配置提取成一个混入对象。简单说就是两个组件都有name和点击事件,混入就是把公用的抽取出来不需要再二次写(局部混入还是要写)

使用步骤:

1.定义混入对象,新建mixin包,里面新建index.js

2.在 index.js中写代码(组件中会用到的,data,methods。。。配置项)
        export const zy ={
            data(){
                return{
                    age:18}},
            //函数写在这里
            methods:{
                showName(){
                    alert(this.name);}},// 没有this.name
            //生命周期
            mounted(){
                console.log('页面挂载会执行');}}
        
3.局部使用(只在当前组件中使用)
          //局部导入mixin混入
          import {zy} from "@/mixin";
          //混入配置项 如果写多个就逗号隔开
          mixins:[zy]
____________________________________
4.全局使用(所有组件都可以用) 
           //在【main.js】中导入mixin混入
           import {zy} from '@/mixin'
           //混入配置项
           Vue.mixin(zy)
           //如果用多个需要配置多个
           // Vue.mixin(zy1)
           // Vue.mixin(zy2)

image

3.Vue插件

使用第三方插件:vue-router、vuex、elementui

功能:用于增强Vue的功能(本来没有某个功能,用了插件后就有了,就可以用this.$xxx取到)
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据

自定义插件使用步骤:

1.新建plugins包,里面新建index.js写下固定代码
    import Vue from "vue";
    import axios from "axios";
    
    export default {
        install(vue) {
            console.log('执行了插件', vue)
            # 可以做的事
            #  1.自定义指令,以后全局都可以使用(不了解没关系)
            #  2.定义全局变量,以后在任何组件中都可以使用到,借助于Vue.prototype往里放 ,以后所有组件只要this.$ajax就是axios对象
            #  3.使用插件的全局混入
            #  4.还可以自定义全局组件(了解)
        }
    }
    
2.在main.js 中配置
   	# 使用自定义插件
    import plugin from '@/plugins'
    Vue.use(plugin)

今后只要看到Vue.use(xxx)那就是在用插件

定义全局变量代码:

plugins>>index.js中

import Vue from "vue"
import axios from "axios"

export default {
    install(vue){
        console.log('执行了插件',vue)
        //定义全局变量  借助prototype往插件里放东西

        //今后所有组件用this.$name都能拿到张三
        Vue.prototype.$name='张三'
        //今后所有组件用this.$add加参数都能拿函数返回值
        Vue.prototype.$add=(a,b)=>{
            return a+b
        }
        //今后所有组件用this.$ajax就等于调用了axios
        Vue.prototype.$ajax=axios
    }
}

image

使用插件的全局混入代码:

plugins>>index.js中

import Vue from "vue"

export default {
    install(vue) {
        console.log('执行了插件', vue)
        //使用插件的全局混入
        Vue.mixin({
            data() {
                return {
                    name: '帅哥'
                }
            }
        })
    }
}

image

4.Vue Router路由跳转

Vue Router是第三方插件,官方推荐用来实现SPA的vue插件

因为目前是单页面应用,想实现在一个Home.html 中有页面跳转效果的插件,其实就是路由控制

<router-link>路由跳转用
<router-view/>替换页面组件用

1)手动在地址上输入某个地址跳转

"""
因为创建vue项目时加入了Router,所以直接用即可
     如果创建时没装Router需要先下载:npm install vue-router --save
     在项目中创建router包>>index.js,代码复制过来,main.js中注册router
"""
1.先在views>>里新建一个页面组件,如:IndexView.vue
2.在router>>index.js>>里的routes数组中写对象,配置路由的跳转(跳转页面组件)
        # 导入IndexView页面组件
        import IndexView from "@/views/IndexView";
    	const routes = [
              {
                path: '/',
                name: 'home',
                component: HomeView
              },
              {
                path: '/index',
                name: 'index',
                component: IndexView
              }
              ]

2)点击按钮跳转路由(路径的两种方式)※

//方式一:标签组件控制   给按钮标签外套一个<router-link to='路径'>标签
        <router-link to="/index">
              <button>点击跳转index页面</button>
        </router-link>


//方式二:js控制   给按钮标签添加点击事件
<button @click="goIndex">点击跳转index页面</button>

  methods: {
       goIndex() {
            this.$router.push('/index')
    },
  },
# js控制的其他小众方法

this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
this.$router.back(): 请求(返回)上一个记录路由
this.$router.go(-1): 请求(返回)上一个记录路由
this.$router.go(1): 请求下一个记录路由
路由跳转时携带数据

以下两种都要理解。【下面举例用js控制路由跳转的方式演示】

#方式一: 带在路径中使用 ? 携带     /course/?pk=1 
'页面组件1:给按钮添加点击事件 路由跳转中用?携带'
  methods: {
       goIndex() {
            this.$router.push('/index/?pk=1')
    },
  },
'页面组件2:想【获取】携带过来的数据,用生命周期钩子函数created来获取''
   created(){
        # 打印在页面控制台中
        console.log(this.$route.query.pk)
   },
——————————————————————
#方式二: 直接写在路径中      /course/1/     
 1.router>>index.js中路径需要修改
        const routes = [
                 {
                    # 携带的数据绑定给id
                    path: '/index/:id',
                    name: 'index',
                    component: IndexView
                },
2.页面组件1:给按钮添加点击事件 路由跳转中直接写在路径中
      methods: {
            goIndex() {
                 this.$router.push('/index/1/')
        },
      },
2.页面组件2:【获取】携带的数据
  created(){
      # 打印在页面控制台中
      console.log(this.$route.params.id)
————————————————————————
 """
 以上如果想让携带的数据:id写或 则需要用字符串的拼接
 this.$router.push('/index/?pk=' + this.变量名)
 this.$router.push('/index/' + this.变量名)
 """ 

补充:区分this.$routethis.$router

this.$router #   new VueRouter对象(实例),可以实现路由的跳转

this.$route  #   是当前路由对象,内部有传入的参数

3)点击按钮跳转路由(对象的两种方式)

this.$router.push('路径')除了可以用路径,还可以使用对象

//方式一:标签控制   给按钮标签外套一个<router-link to='对象'>标签
    <router-link :to="{name: 'index', query: {name: 'lqz'}, params: {pk: 10}}">
      <button>点我跳转到index页面</button>
    </router-link>

//获取传入的id信息
this.$route.params.pk

——————————————————————————————————

//方式二:js控制   给按钮标签添加点击事件 路由里用对象方式传入
  methods: {
    goIndex() {
      this.$router.push({
            name:'index',
            query:{name:'zy',},
            params:{pk:1}
      })
    },
  },
      
//获取传入的id信息
this.$route.params.pk

image

4)路由守卫

全局路由守卫:一般用在后台管理中
-前置路由守卫:在进路由前,执行代码。(做权限判断,否和条件给跳转)
-后置路由守卫:路由跳转走,执行代码(不常用)

以下仅了解全局路由守卫中的前置路由守卫

使用:router>>index.js 下的const router=xxxx下加入代码即可

// 全局前置路由守卫:【任意路由跳转】都会触发它的执行 包括根路径
router.beforeEach((to, from, next) => {
    // to 去哪个路由对象
    // from 来自哪个路由对象
    // next 是函数,如果加括号执行就会真正的过去
    var res = localStorage.getItem('userinfo')
    if (to.name == 'login') {
        alert('可以进首页')
        next()
    } else if (res) {
        next()
    } else {
        alert('您没有登录')
        // 跳转到login
        router.push({name: 'login', params: {id: 1}})
    }
})

5.Vuex状态管理器

在Vue中实现集中式状态(数据)管理的一个Vue插件,存放变量的。对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信

多个组件需要共享数据时就需要用到vuex

image

使用:

1.src>>store>>index.js中给state里加一个num:10
2 在index.js 中写三个状态
        export default new Vuex.Store({
            state: {
                # 真正放数据的
            },
            mutations: {
                # 放方法,正常是让actions中来调用
                # 组件也可以直接调用
            },
            actions: {
                # 放方法,判断跟后端交互,调用mutations的地方
            }
        })
3.在组件中也可以显示state的变量
    html中:
      {{$store.state.变量名}}
    js中:
      this.$store.state.变量名

代码:

HomeView.vue

<template>
  <div class="home">
    <h1>Vuex的使用</h1>
    <button @click="add">点击右侧自增1</button>--->>{{$store.state.num}}
  </div>
</template>
<script>

export default {
  name: 'HomeView',
  methods:{
    add(){
      //1.先触发actions的执行:调用dispatch触发里面定义的AddNum函数,并把1传给value
      this.$store.dispatch('AddNum',1)
    }
  }
}
</script>

store>>index.js

export default new Vuex.Store({
    state: {
        num: 10
    },
    
    mutations: {
        AddNum(state,value){
            //3.这里就可以操作state里的数字让它加value(1)
            state.num= this.state.num+value
        }
    },
    
    actions: {
        AddNum(context, value) {
            //2.再触发mutations的执行:调用commit触发里面定义的AddNum函数,并把value传给value
            context.commit('AddNum',value)
        }
    },
})

这就实现了在上图中所画的:state里的数字需要调用actions操作,actions再调用mutations去操作state里的数字。之所以绕一大圈是因为在actions里可以发送ajax请求!!(如果不需要发也可以跳过中间步骤直接去改)如下面案例:

案例:添加购物车数量案例

components>>shopcar.vue

<template>
<div>
  购物车数量{{$store.state.good_num}}
</div>
</template>

views>>HomeView.vue

<template>
  <div class="home">
    <h1>Vuex的使用</h1>
    <ul>
      <li v-for="item in good_list">
        商品id:{{item.id}},商品名:{{item.name}},商品价格:{{item.price}}
        <button @click="handleadd(item.id)">加入购物车</button>
      </li>
    </ul>
    <hr>
    <shopcar></shopcar>
  </div>
</template>
<script>
//导入购物车组件
import shopcar from "@/components/shopcar";

export default {
  name: 'HomeView',
  data(){
    return{
      good_list:[
          {id:1,name:'牛肉',price:10},
          {id:2,name:'羊肉',price:20},
          {id:3,name:'猪肉',price:30},
      ],
    }
  },
  methods:{
    handleadd(id){
      this.$store.dispatch('shopcar',id)
    },
  },
  components:{
    shopcar
  },
}
</script>

store>>index.js

import axios from "axios";


export default new Vuex.Store({
    state: {
        good_num:0
    },
    mutations: {
        shopcar(state){
            state.good_num+=1
        }

    },
    actions: {
        shopcar(context,id){
            //假设发送了ajax请求把id携带到后端加入到购物车数据库中
            context.commit('shopcar')
            }
    },
})

image

6.localStorage系列

以下三种都是在浏览器存储数据的,如:

1.登录成功后token要存在本地,可以存在这三个任意一个中

2.如迪卡侬官网,不登陆把商品添加购物车关掉页面再打开购物车中还有东西,虽然没和后端交互但是存储了数据(迪卡侬存到了localStorage中)

3.组件间通信(跨组件)

三者区别:

# localStorage
    '【永久存储】除非清空缓存、手动删除、代码删除'
    
    # 写入数据,值必须是字符串不可以是对象,要转成JSON格式字符串
    localStorage.setItem('userinfo', JSON.stringify(this.userinfo))  # 写入json格式字符串
    JSON.parse(localStorage.getItem('userinfo'))  #获取对象
    localStorage.clear()  # 清空全部
    localStorage.removeItem('userinfo')  # 删除指定
    
    """
    JSON.stringify:把对象转成json格式字符串
    JSON.parse:把json格式字符串转对象
    """
    
————————————————————————————

# sessionStorage
    '【关闭浏览器自动清理】'
    sessionStorage.setItem('userinfo', JSON.stringify(this.userInfo))
    JSON.parse(sessionStorage.getItem('userinfo'))  #获取对象
    sessionStorage.clear()  # 清空全部
    sessionStorage.removeItem('userinfo') 
    
————————————————————————————

# cookie  
	'【到过期时间自动清理】'
    # vue操作cookie需借助于第三方vue-cookies
    # cnpm install vue-cookies  
    import cookies from 'vue-cookies'
    cookies.set('userinfo', JSON.stringify(this.userInfo))
    cookies.get('userinfo')
    cookies.remove('userinfo')

image

八.ElementUi(重点)

在vue上css样式用的最多的是elementui,但是还有其他的:
-elementui 做网页端样式用的多,主要给vue2的
-elementui-plus 第三方团队写的,主要给vue3的
-vant 做app的样式
————以上几种够用了 下面的可以了解—————
-iview 主要做后台管理的 www.iviewui.com

下载与使用

1.安装
    	cnpm i element-ui -S
    
2.配置'完整引入',在 【main.js】 中写入以下内容
        import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        Vue.use(ElementUI)  #以后在组件中直接使用elementui提供的【全局组件】即可
        
3.在组件中使用
    	去官网找到需要的,复制代码贴到项目中皆可,有时候还需要贴样式
         https://element.eleme.cn/#/zh-CN/component/installation

代码演示:

HomeView.vue

elementui 栅格是24

<template>
  <div class="home">
    <h1>栅格系统 左右布局</h1>
    <el-row>
      <el-col :span="12">
        <div class="left">aaa</div>
      </el-col>
      <el-col :span="12">
        <div class="right">bbb</div>
      </el-col>
    </el-row>
  </div>
</template>

<!--查找标签改变背景颜色-->
<style scoped>
.left{
  background-color: red;
}
.right{
  background-color: blue;
}
</style>

image

九.Vue3

1.vue3介绍

公司中老项目在用vue2版本,新项目都在用vue3。

vue3的变化:

1.性能的提升
    	-打包大小减少41%
    	-初次渲染快55%, 更新渲染快133%
    	-内存减少54%
        
2.源码的升级
    	使用Proxy代替defineProperty实现响应式
    	重写虚拟DOM的实现和Tree-Shaking
        
3.拥抱TypeScript
    	Vue3可以更好的支持TypeScript
    
4.新的特性
    	Composition API(组合API)
            setup配置
            ref与reactive
            watch与watchEffect
            provide与inject
        新的内置组件
            Fragment
            Teleport
            Suspense
        其他改变
            新的生命周期钩子
            data 选项应始终被声明为一个函数
            移除keyCode支持作为 v-on 的修饰符
        
5.'组合式API和配置项API'
	vue2用的是【配置项API】:
         new Vue({
             el:'#app',
             data:{}
         })
        
    vue3:用的是【组合式API】:#不再要求写在data、methods里
        let name='lqz'
        let add=()=>{ 
        }

2.vue3创建项目的两种方式

1)使用vue-cli创建(后面我们用该方法讲解)

# 创建跟之前vue2 一样,只是选择vue版本的时候,选择vue3

# 创建完成,使用pycharm打开,并运行

创建完成后观察项目结构发现src>>main.js中的Vue实例对象变成了

createApp(App).use(store).use(router).mount('#app')这叫链式调用

官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html

2)使用vite创建(推荐)

是新建的前端构建工具,最大的优势就是【速度快】

官网:https://vitejs.cn/

官方中文文档:https://cn.vitejs.dev/guide/

需注意:vite只能创建vue3

官网提供的用vite构建vue项目:
https://cn.vuejs.org/guide/scaling-up/tooling.html#project-scaffolding
————————————————————————————————————
# 使用步骤:
1.安装 :npm init vue@latest
    '注意路径,这是新建项目'
2.项目创建好用pycharm打开然后命令行下载第三方依赖
    'cd到项目中 执行cnpm install'
4.运行:npm run dev 或 上方添加npm 名字serve运行都可以

image

为什么vite创建的项目快?

1.因为它不安装第三方依赖!需要我们项目创建好后自行安装
2.执行项目都是热更新:就是按需编译,当访问哪个页面时就编译哪个页面,其他页面只要不访问就不会被编译

image

image

3)纯净版

src>>app.vue

<template>
      <router-view/>
</template>

src>>views>>AboutView.vue可以删掉,需要时再创建

src>>router>>index.js里把AboutView.vue路由删掉

src>>views>>HomeView.vue

<template>
      <div class="home"></div>
</template>
<script>
    export default {
      name: 'HomeView',
    }
</script>

src>>components里的小组件可以都删掉,需要时再创建

4)vue2与vue3其他区别

vue2:
new Vew()---->是Vue的实例,实例里有:$store,$refs...

vue3:
createApp(App)--->是个对象,对象里没有:$store,$refs...以后都是导入使用

且今后vue3的<template>里不需要写在一个标签里了

配置项api也不用了,使用组合式api。核心的就是setup函数

3.setup函数

以后setup(){}里写组合式api,里面可以定义变量、函数,但一定要return才能在<template>中使用

以后定义变量用let 常量用const 尽量不要用var

且setup中没有this,使用变量可直接用,但是失去了响应式(页面不能实时加载更新后的数据)!

<template>
  <div class="home">
        <h4>写变量:{{num}}</h4>
        <button @click="handleClick">点击数字+1</button>
  </div>
</template>

<script>
export default {
  name: 'HomeView',
  //在setup中写组合式api
  setup(){
    let num = 10
    //由于setup中没有this,可直接使用变量
    let handleClick=()=>{
      num++
    }
    //变量、函数都需要return出去才能用
    return {num,handleClick}
  },
}
</script>

image

如果想让它有响应式就用到了ref

4.ref和reactive

不同类型做响应式

ref通常用来包裹数字字符串类型,操作数据:需要变量名.value,读取数据:不需要.value
reactive用来包裹数组对象类型 操作数据与读取数据:不需要.value

ref:以后定义变量和函数如果想有响应式就用ref包起来再修改变量和函数,需要用 变量名.value 修改

<template>
      <div class="home">
            <h4>写变量:{{ num }}</h4>
            <button @click="handleClick">点击数字+1</button>
      </div>
</template>

<script>
//导入ref
import {ref} from 'vue'
    
export default {
      name: 'HomeView',
      //在setup中写组合式api
      setup() {
        //用ref包起来要加响应式的变量
        let num = ref(10)
        let handleClick = () => {
          //用了ref后修改变量就需要用变量名.value修改
          num.value=num.value+1
        }
        //变量、函数都需要return出去才能用
        return {num, handleClick}
      },
}
</script>

image

reactive:以后定义数组和对象如果想有响应式就用reactive包起来再修改数组和对象,不需要.value

<template>
  <div class="home">
    <h4>{{ userInfo.name }}</h4>
    <h4>{{ userInfo.num }}</h4>
    <button @click="handleClick">点击数字+1</button>
  </div>
</template>

<script>
import {reactive} from 'vue'

export default {
  name: 'HomeView',
  //在setup中写组合式api
  setup() {
    //用reactive包起来要加响应式的对象
    let userInfo = reactive({
      name: '张三',
      num: 10
    })
    let handleClick = ()=>{
      userInfo.num=userInfo.num+1
    }
    //需要return出去才能用
    return {userInfo,handleClick}
  },
}
</script>

image

配置项api和组合式api可以混写,不建议

在setup中定义的变量、函数,在之前配置项api中可以直接使用this.变量,函数调用即可。但是在原来配置项中定义的变量、函数,在setup中无法使用

5.计算属性和监听属性

1)计算属性

vue2中计算属性要求写在computed的配置项中,但是vue3不建议这样写

<template>
  <div class="home">
    <p>姓:<input type="text" v-model="nameInfo.firstname"></p>
    <p>名:<input type="text" v-model="nameInfo.lastname"></p>
    <p>全名:{{newName}}</p>
  </div>
</template>

<script>
import {reactive,computed} from 'vue'

export default {
  name: 'HomeView',
  //在setup中写组合式api
  setup() {
    let nameInfo=reactive({
      firstname:'',
      lastname:''
    })
    //由于建议写在配置项中 所以写函数,函数的返回值是一个计算属性
    let newName=computed(()=>{
      return nameInfo.firstname+nameInfo.lastname
    })
    //需要return出去才能用
    return {nameInfo,newName}
  },
}
</script>

image

额外:计算属性取值和修改值(了解)

    let newName = computed({
      //获取值触发get的执行
      get() {
        return newName.firstName + newName.lastName
      },
      //修改值触发set的执行(会自动传入修改后的值)
      set(value) {
        //截取第一个字
        newName.firstName = value.slice(0, 1)
        //截取第二个字往后到结尾
        newName.lastName = value.slice(1)
      },
    })

2)监听属性

同样vue3对监听属性也有新的写法

只要name发生变化,就会触发匿名函数执行:

组合式api写法

<template>
  <div class="home">
    <h4>num:{{num}}</h4>
    <button @click="handleClick">点击数字+1</button>

  </div>
</template>
<script>
import {ref, watch} from 'vue'

export default {
  name: 'HomeView',
  //在setup中写组合式api
  setup() {
    let num=ref(10)
    let handleClick=()=>{
      num.value=num.value+1
    }
    //只要name发生变化就触发某个函数的执行
    watch(num,(newNum,oldNum)=>{
      console.log('num变了')
      console.log('现在的:',newNum)
      console.log('以前的:',oldNum)
    })
    //需要return出去才能用
    return {num,handleClick}
  },
}
</script>

image

watchEffect 用法,只要num变化就会执行,num不变化就不会执行vue3提供的方式(了解即可 用的少)

 watchEffect(() => {
      console.log(age.value)
    })

6.生命周期

image

  • Vue3中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • 但是Vue3不推荐配置项写法,建议写在setup中
    • beforeCreate>>>>setup() 直接在setup中写
    • created>>>>>>>setup() 直接在setup中写
    • beforeMount >>>>onBeforeMount 需要import {on...} from 'vue'进来在setup中写
    • mounted>>>>>>>onMounted
    • beforeUpdate>>>>onBeforeUpdate
    • updated >>>>>>>onUpdated
    • beforeUnmount >>>onBeforeUnmount
    • unmounted >>>>>onUnmounted
import {onBeforeMount} from 'vue'
setup(){
        let show = ref(false)
        onBeforeMount(()=>{
              console.log('onBeforeMount')
        })
 }

如果是向后端发送ajax请求则:

setup(){
        let show = ref(false)
        axios.get().then(res=>{
              show.value=res.data.show
        })
 }

7.toRefs

...toRefs(data)解构赋值,只能解构对象

今后ruturn的返回值可以在模板上直接使用,如:{{name}}

<template>
  <div class="home">
    {{name}}
    {{age}}
    <button @click="handleClick">点击年龄+1</button>
  </div>
</template>
<script>
import {toRefs,reactive} from 'vue'

export default {
  name: 'HomeView',
  //以后setup的返回值可以直接使用
  setup() {
    let data = reactive({
      name: 'zy',
      age: 19
    })
    let handleClick=()=>{
      console.log(data.age++)
    }
    //以后在模板中直接用  {{name}}
    return {...toRefs(data),handleClick}
  }
}
</script>

image

8.<script setup>的作用和lang=ts

用vite创建的项目中

<script setup>
        import {ref} from 'vue'
        let name = ref('zy')
        let handleClick = () => {
          alert('hi')
        }
</script>

<template>
    <main>
            <h1>{{name}}</h1>
        <button @click="handleClick">点击</button>
    </main>
</template>

<!--以后 这个script中的代码就相当于放在了setup函数中,不用return了-->
——————————————————————————
<script setup lang=ts>
//里面代码使用ts写,而ts完全兼容js,继续写js代码也没问题

十.vue后台管理模板

# 公司内部项目,会写后台管理,往上有很多vue开源的后台管理的框架,咱们只需要在上面做二次开发

# django-vue-admin 前端---》D2Admin 
# 若依:vue前端  用来   vue-element-admin
# elamin前端   drf  写了基于rbac权限管理的框架

# https://gitee.com/liuqingzheng/vue_admin
posted @ 2023-02-15 22:30  oreox  阅读(35)  评论(0编辑  收藏  举报