Vue


一、IDEA中安装vue插件

让idea中编写vue组件/脚本具有代码提示、着色等功能

  • File -> settings... 打开设置窗口
    • 搜索 plugin -> 点击Marketplace ->搜索vue -> install

  • 关于搜不到插件怎么办?
    • 百度一下,有说设置代理的,有说关防火墙的,有说将网络换成热点的,我试了下都没什么卵用,同学们可以试试
    • 毕竟我们用的是破解版,实在不能在线安装的话我们可以进行离线安装插件
  • 当你不能在线安装,离线安装Vue插件方法如下
    • 进入idea插件市场:https://plugins.jetbrains.com/ ,搜索 vue

  • 找到下图这个vue,点进去

  • 切换到版本标签页

  • 然后根据自己的IDEA版本,下载适合自己IDEA版本的vue插件,我这里的idea是 2019.3.5版本的

  • 让后进入你的idea插件安装窗口,选择 插件来源于你的本地磁盘安装

  • 然后找到你刚刚下载的vue插件包,选择它,点击ok

  • 最后点击 应用,然后重启IDEA生效

二、VUE 3 基础

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

Vue 只关注视图层, 采用自底向上增量开发的设计。

Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

官方文档:https://v3.cn.vuejs.org/api/

渐进式框架?

  • 就是一开始不需要你完全掌握它的全部功能特性,可以后续逐步增加功能。没有多做职责之外的事情

例如,使用Angular,必须接受以下东西

  • 必须使用它的模块机制
  • 必须使用它的依赖注入
  • 必须使用它的特殊形式定义组件

所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰

又例如,使用React,你必须理解

  • 函数式编程的理念
  • 需要知道它的副作用
  • 什么是纯函数
  • 如何隔离、避免副作用

它的侵入性看似没有Angular那么强,主要因为它是属于软性侵入的

1、安装方式

1.1、直接引入


<script type="text/javascript" src="./js/vue-3.2.4.global.js"></script>

2、模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

Vue.js 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统。

结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。

使用格式

  • options一个js对象,后面会详细介绍它
  • element 此vue模板语法应用于指定的element元素上
Vue.createApp(options).mount(element);

2.1、简单示例

  • html部分

<script type="text/javascript" src="./js/vue-3.2.4.global.js"></script>

    姓名:{{name}} 
调用getName方法:{{getName()}}
  • js部分
    • data属性 :重要,数据绑定
    • methods属性:重要,函数绑定
<script>
    //每一个vue应用都需要,创建一个vue对象
    let app = Vue.createApp({
        //数据绑定
        data:function(){
            return {
                name:'张三'
            };
        },
        //函数绑定
        methods:{
            getName:function(){
                return "function--"+this.name;
            }
        }
    });
    //该vue对象内容渲染(挂载)到哪个个元素上
    //指定元素时,可使用类似jquery选择器的表达式
    app.mount('#app')
</script>

2.2、vue指令

2.2.1、渲染html

2.2.1.1、v-html

渲染指定文本为html

2.2.1.2、v-show

true则显示,false隐藏

2.2.2、属性绑定

2.2.2.1、v-bind:属性名

用于将数据对象中指定的变量绑定到html元素属性值上

v-bind:xxx = "" 可缩写为 :xxx = ""

示例1:

v-bind:class //绑定class属性值
v-bind:href  //绑定href属性值
v-bind:value //绑定value属性值

示例2:

//绑定对象
v-bind:class="{'clsName':boolean}" //boolean为true,那么使用clsName样式,为false不会使用clsName这个样式
//绑定数组
v-bind:class="['clsName1','clsName2'...]"
//内联样式绑定对象
v-bind:style="{color:'red',fontSize:'20px'}"

2.2.3、双向数据绑定

2.2.3.1、v-model

双向数据绑定,使用于 input、select、textarea、checkbox、radio元素上,用于更改input框中的值同时vue也会修改js对象中绑定的变量的值

常用修饰符

  • .number//将文本框输入的如果是正确的数字,那么会转换为number类型给变量,不是正确的数字则转换失败,变量值NaN
  • .trim //将文本框输入的字符串去掉2端空格后再给变量

示例1:修改input框中的值,看看变化

  • html

    姓名:{{name}} 
  • options对象
{
    //数据绑定
    data:function(){
        return {
            name:'张三'
        };
    }
}

示例2:配合修饰符使用


2.2.4、流程控制指令

2.2.4.1、条件
v-if ...
v-else-if ...
v-else ...

示例:

2.2.4.2、循环
//遍历数组
v-for="obj in 数组"
//遍历对象
v-for="(key,value,index) in 对象"
//遍历数字
v-for="n in 10"

示例:

2.3、数据对象和方法对象

  • options对象如下
{
    //数据对象
    data(){
        return {
            //此处可自定义属性,被挂载的html元素可直接使用这里面的属性
        }
    },
    //方法对象
    methods:{
		//此处可自定义方法,被挂载的html元素可直接调用这里面的方法
    }
}

示例:

2.4、计算属性

和methods功能几乎一样

computed(计算) 与 methods(方法)区别?

  • computed基于浏览器缓存,只有依赖的属性值发生变化时才会调用函数重新计算
  • methods 只要相关dom被渲染,就会调用

示例:

  • options对象如下
//app对象如下
{
  data:function(){
    return {
	//状态
	status:1
    };
  },
  computed:{
	//计算属性
	statusZh:function(){
		//this 访问Vue自身对象
		if(this.status == 1){
			return '启用';
		}else{
			return '禁用';
		}
	}
  }
}
  • html中使用它
//使用方式
{{statusZh}}

2.5、过滤器

  • html中使用过滤器
{{数据 | dateFormat(p1,p2...) }}
  • options对象如下
{
    filters:{
        //过滤器函数
        dateFormat:function(value,p1,p2...){

        }	
    }
}

示例1:将字符串转大写的过滤器


示例2:将字符串数组以指定的 分割符拼接起来 的过滤器

示例3:将时间戳 转换为指定格式的字符串的 过滤器

2.6、监听

监听指定属性发生变化

示例:

  • options对象如下
{
  data:function(){
    return {
	//计数
	count:1
    };
  },
  watch:{
	//监听count变量变化
	//newVal为变化后的新值,oldVal为之前的值
	count:function(newVal,oldVal){
		console.info('新值:',newVal,'旧值:',oldVal);
	}
  }
}

2.7、事件绑定

当触发指定事件时,会调用绑定的函数

使用范例:

v-on:click="" //绑定点击事件
v-on:blur=""  //绑定鼠标聚焦事件
....

缩写:

v-on:xxx =""   可缩写为  @xxx = ""

常见的事件修饰符

  • .stop //阻止事件冒泡
//示例,这样写会阻止点击事件冒泡(传递)到父元素上
@click.stop="" 
  • .self //只监听自身元素所触发的事件
//示例,这样写 子元素冒泡上来的点击事件就无法触发它调用的函数
@click.self = "func1()"

示例1:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    

  
  
  <script type="text/javascript" src="./js/vue-3.2.4.global.js"></script>
  
      
div1
div2
<script> //每一个vue应用都需要,创建一个vue对象 let vm = Vue.createApp({ //函数绑定 methods:{ fuc1:function(event){ console.info("div1被点击"); console.info("如果绑定时只指定了函数名称,那么被绑定的函数参数可以获取原生的js事件对象") console.info("event:",event) }, fuc2:function(p1){ console.info("div2被点击"); console.info("如果绑定时只指定了参数值,那么被绑定的函数可以获取传递过来的参数值") console.info("p1:",p1) } } }); //该vue对象内容渲染(挂载)到哪个个元素上 //指定元素时,可使用类似jquery选择器的表达式 vm.mount('#app') </script>

示例2:配合事件修饰符使用,沿用上面的例子,html如下


    
div1
div2

2.8、组件

组件(Component)是 Vue.js 最强大的功能之一。

组件可以扩展 HTML 元素,封装可重用的代码。

注意:这里要额外拓展ES6中 import、export语法

  • a.js 文件中定义了一个对象,使用export可将此对象导出
let obj = {
	name:"张三"    
}
//导出obj对象
export default  obj;
  • index.html中导入此对象
  • 注意设置:type="module" 才能使用import语句

<script type="module">
  //import 为ES6 语法,可引入其他js文件
  import obj from "./a.js";
  //打印看看
  console.info('obj:',obj);
</script>

2.8.1、全局组件

注:示例3重要,必须示例

示例1:简单注册

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  <script type="text/javascript" src="./js/vue-3.2.4.global.js"></script>
  
    
    
  
  <script>
    //每一个vue应用都需要,创建一个vue对象
    let app = Vue.createApp({
    });
    //注册全局组件
    app.component("global",{
      template:"

我是全局组件,一经注册,Vue中无论哪里,都可以使用

" }); //该vue对象内容渲染(挂载)到哪个个元素上 //指定元素时,可使用类似jquery选择器的表达式 //其实这个被挂载的app元素,就是整个vue中的根组件 app.mount('#app') </script>

示例2:制作一个页头的全局组件

Header.js内容如下

//页面内容
let html = `
{{desc}}
`; //组件对象 let Header = { //模板字符串 template:html, data:function(){ return { desc:"我是页头" } } }; //默认导出 Header对象 export default Header;

html使用组件代码如下

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
    
  
  
  <script type="text/javascript" src="./js/vue-3.2.4.global.js"></script>
  
    
    
  
  <script type="module">
    //import 为ES6 语法,可引入其他js文件
    //注意:要使用import语句,必须设置script 的type为 module,否则会报错,script的type默认为text/javascript
    //导入Add.js 中的对象
    import Header from "./js/Header.js";
    //每一个vue应用都需要,创建一个vue对象
    let app = Vue.createApp({});
    //注册全局组件 ,注意:组件id不能与已有的标签同名
    app.component("hd",Header);

    //该vue对象内容渲染(挂载)到哪个个元素上
    //指定元素时,可使用类似jquery选择器的表达式
    //其实这个被挂载的app元素,就是整个vue中的根组件
    app.mount('#app')
  </script>

示例3:制作一个页尾的全局组件

注:

  • 由于ES6提供的 import 语句只能引入的js文件,然而我们在js文件中编写html代码极为不便,而且样式也不好书写
  • 有大佬写了一个 vue3-sfc-loader.js(vue组件加载器)的脚本,用于加载vue组件,只要你按照规定的格式书写组件并注册即可
  • 编写 Footer.vue组件,代码如下(注意规则)
    • template 标签中包含的是html模板代码
    • script 标签中包含的是js代码
    • style 标签中包含的css代码,scoped属性指定此style为局部作用



<script>
    let Footer = {
        data:function(){
            return {
                desc:"我是页脚"
            }
        }
    };
    //特别注意:使用http-vue-loader加载器加载vue组件时不能用export来导出对象
    //export default Footer;
    //导出对象
    module.exports = Footer;
</script>


  • html中注册组件并使用,代码如下
    • 注意1:vue-3.2.4.global.js、vue3-sfc-loader.js、vue3-loader-helper.js必须按照顺序引入
    • 注意2:注册由loadModule函数加载的组件,必须使用 Vue.defineAsyncComponent来异步注册组件
<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
    
    
    
  
  <script type="module">
    //import 为ES6 语法,可引入其他js文件
    //注意:要使用import语句,必须设置script 的type为 module,否则会报错,script的type默认为text/javascript
    //导入Add.js 中的对象
    import Header from "./js/Header.js";
    //使用Vue组件加载器 加载我们的 页脚组件
    let Footer = loadModule("./components/Footer.vue");

    //每一个vue应用都需要,创建一个vue对象
    let app = Vue.createApp({
    });
    //注册全局组件 ,注意:组件id不能与已有的标签同名
    app.component("hd",Header);
    //异步注册页脚组件 为全局组件
    app.component("ft", Vue.defineAsyncComponent(function(){
      return Footer;
    }));

    //该vue对象内容渲染(挂载)到哪个个元素上
    //指定元素时,可使用类似jquery选择器的表达式
    //其实这个被挂载的app元素,就是整个vue中的根组件
    app.mount('#app')
  </script>

2.8.2、局部组件

<script> //组件A let componentA = { ... }; //组件B let componentB = { ... }; let app = Vue.createApp({ //局部组件注册 components:{ 'component-a':componentA, 'component-b':componentB } }); app.mount('#app'); </script>

示例1:制作用户添加表单的局部组件

  • Add.vue 代码如下
//html模板语句部分


<script>
    //定义组件相关数据以及绑定内容
    let add = {
        //数据绑定
        data: function () {
            return {
                name: "张三",
                age: 18,
                tel: "123456789",
                hobbyList:['01','02']
            }
        },
        methods:{
            //点击提交按钮 ,将调用的函数
            clkSubmit:function(){
                console.info('表单数据-----:');
                console.info('name:',this.name);
                console.info('age:',this.age);
                console.info('tel:',this.tel);
                console.info('hobbyList:',this.hobbyList);
            }
        }
    };
    //es6语法,使用import引用此文件时,默认导出的js对象为 add对象
    export default add;
</script>


  • html中 注册局部组件并使用代码如下
<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
    
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  <script type="module">
    //导入Add组件
    let Add = loadModule('./components/Add.vue');
    //每一个vue应用都需要,创建一个vue对象
    let app = Vue.createApp({
      //注册局部组件
      components:{
        //异步加载组件
        'user-add':Vue.defineAsyncComponent(function(){
          return Add;
        })
      }
    });

    //其实这个被挂载的app元素,就是整个vue中的根组件
    app.mount('#app')
  </script>

2.8.3、组件关系

2.8.3.1、父子组件

Vue是最大的祖父(app.mount('#app') 这个 app元素就是最大的祖父)

组件可以无限嵌套

组件在哪里被使用了,那个被使用的组件就是儿子

2.8.3.2、父子组件通信案例

父组件传递数据给子组件:

  • 子组件的options中可定义 props 来接收父组件的传递的数据。

  • 子组件options范例

{
  props:{
    // type可以是:String、Number、Boolean、Array、Object、Date、Function、Symbol(ES6新增的类型,表示独一无二的值)等类型
    p1:{
       type:String,//必须是字符串类型
       required:true,//要求父组件必须传递该属性值
       default:function(){ return "" } //默认是空字符串
    },
    p2:{
       type:Number,//必须是数字类型
       default: 100,//如果父组件没有传递值,则默认为100
    }
  }
}
  • 父组件在子组件标签上 通过 v-bind:xxx="变量名" 来将数据绑定到子组件对应的属性上
  • xxx 为子组件props中定义的属性名。注意:子组件的属性名若是驼峰命名绑定时需要转 - ,下面的案例中会提到
  • 变量名可在父组件 data中定义

子组件发送消息给父组件:

  • 子组件可利用$emit函数来发送一个事件给父组件,然后在父组件中监听此事件
/**子组件**/
//第一个参数为事件名称,后续的参数为传递的内容,内容参数可以有多个
this.$emit("eventName",p1...)
/**子组件**/

/**父组件**/
//父组件则需要监听子组件发送的事件,比如监听子组件的eventName事件,示例如下
<子组件 v-on:eventName="func1"> 
//注意,v-on:后面的eventName就是子组件发送的事件名称
//当子组件发送 eventName 这个事件给父组件时,父组件就会调用 func1这个函数来进行处理
/**父组件**/

案例:曹操和曹丕对话

  • 儿子组件-曹丕 Caopi.vue 文件代码如下


<script >
    export default {
        //定义属性,可用于接收父组件传递的值
        props:{
            //定义了一个属性,名称为:father-say
            "fatherSay":"",
        },
        data:function(){
            //告诉爹的的言语
            tellDad:""
        },
        methods:{
            //发送消息给父亲
            sendMsg:function(){
                console.info("*** 曹丕点击了发送按钮")
                //$emit为发送自定义事件函数
                //下面的代码是自定了 caopiMsg事件,并触发该事件,而且将tellDad数据传递给了该事件
                this.$emit("caopiMsg",this.tellDad);
            }
        }
    }
</script>
  • 父组件-曹操,html代码如下
<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

祖父:曹操

对曹丕说的话:
曹丕的回信:{{cpMsg}}

<script type="module"> //导入CaoPi组件 let Cp = loadModule("./components/CaoPi.vue"); let app = Vue.createApp({ components:{ //异步注册曹丕组件 Cp:Vue.defineAsyncComponent(function(){ return Cp; }) }, data:function(){ return { //曹操对曹丕说的话 tellCp:"", //接收曹丕消息的变量 cpMsg:"" } }, methods:{ //曹丕的消息 caopiMsg:function(msg){ this.cpMsg = msg; console.info("*** 曹丕消息:",msg); } } }); //将app对象挂载(渲染)到指定的dom元素上 let vm = app.mount('#app') </script>
2.8.3.3、兄弟组件及通信

在一个组件中同时使用了组件A和组件B,A、B就是兄弟组件

兄弟组件通信范例:沿用上面的例子,在做一个甄姬组件,和曹丕为兄弟组件,且曹丕和甄姬可互相对话

说明:

  • 曹丕发消息给甄姬原理:在甄姬在根vue对象挂一个回调函数,曹丕想传递消息给甄姬时调用此函数即可
  • 甄姬发消息给曹丕原理同上
  • Caopi.vue(曹丕组件)完整代码如下


<script >
    export default {
        //定义属性,可用于接收父组件传递的值
        props:{
            //定义了一个属性,名称为:father-say
            "fatherSay":"",
        },
        data:function(){
            return {
                //告诉爹的的言语
                tellDad:"",
                //告诉给甄姬的言语
                tellZj:""
            }
        },
        methods:{
            //发送消息给父亲
            sendMsg:function(){
                console.info("*** 曹丕点击了发送按钮")
                //$emit为发送自定义事件函数
                //下面的代码是自定了 caopiMsg事件,并触发该事件,而且将tellDad数据传递给了该事件
                this.$emit("caopiMsg",this.tellDad);
            },
            //发送消息给甄姬
            sendMsgToZj:function(){
                console.info("*** 曹丕发送消息给甄姬")
                //根对象,甄姬在根Vue对象挂了一个回调函数,曹丕可通过此回调函数将言语传递给甄姬
                //let root = this.$root;
                console.info('call func',this.$root.callCpTake)
                //如果此回调函数存在
                if(this.$root.callCpTake){
                    //调用此函数,并将消息传递过去
                    this.$root.callCpTake(this.tellZj);
                }
            }
        }
    }
</script>
  • Zhenji.vue(甄姬组件)完整代码如下


<script>
    export default {
        data:function(){
            return {
                //来自于曹丕的消息
                cpMsg:""
            }
        },
        //当组件初始化options完毕后
        created:function(){
            let self = this;
            //this.$root 可获取到 根Vue对象
            //在根对象挂一个回调函数
            this.$root.callCpTake = function(msg){
                //曹丕说话的回调函数,我们约定曹丕每次说话就调用此函数
                self.cpMsg = msg;
            }
        },

    }

</script>
  • 曹操组件html完整代码如下
<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

祖父:曹操

对曹丕说的话:
曹丕的回信:{{cpMsg}}


<script type="module"> //导入CaoPi组件 let Cp = loadModule("./components/CaoPi.vue"); //导入ZhenJi组件 let Zj = loadModule("./components/ZhenJi.vue"); let app = Vue.createApp({ components:{ //异步注册曹丕组件 Cp:Vue.defineAsyncComponent(function(){ return Cp; }), //异步注册甄姬组件 Zj:Vue.defineAsyncComponent(function(){ return Zj; }) }, data:function(){ return { //曹操对曹丕说的话 tellCp:"", //接收曹丕消息的变量 cpMsg:"" } }, methods:{ //曹丕的消息 caopiMsg:function(msg){ this.cpMsg = msg; console.info("*** 曹丕消息:",msg); } } }); //将app对象挂载(渲染)到指定的dom元素上 let vm = app.mount('#app') </script>

2.8.4、组件生命周期

2.8.4.1、beforeCreate

类型:Function

详细:在实例初始化之后、进行数据侦听和事件/侦听器的配置之前同步调用。

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

生命周期演示

页面内容
<script type="module"> let app = Vue.createApp({ data:function(){ return { name:"张三" } }, /* Vue.createApp 函数会创建Vue对象,当Vue对象创建完毕后 此时仅仅初始化了一些内置事件和生命周期的函数 */ beforeCreate:function(){ //这个用得不多 //注意:这是 data、methods、watch...等都没有初始化,这里是无法获取的 //打印的undefined console.info('name:',this.name); } }); //将app对象挂载(渲染)到指定的dom元素上 app.mount('#app') </script>
2.8.4.2、created

类型:Function

详细:在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el 属性目前尚不可用。

注:$el指的是虚拟dom,可通过此虚拟dom访问html元素

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

生命周期演示

页面内容,姓名:{{name}}
<script type="module"> let app = Vue.createApp({ data:function(){ return { name:"张三" } }, /* 当options选项被处理完毕后调用此函数,options处理完毕后意味着 data、methods、watch...等数据都可以获取到了 但是注意:这时dom还没有被挂载(渲染),这时是拿不到dom元素的 此函数一般在首次进入页面需要加载数据时使用 */ created:function(){ //这里打印的是 “张三” console.info("name:",this.name); //这时虚拟dom还未创建,dom也还没有挂载(渲染),打印的null console.info("--虚拟dom:",this.$el) } }); //将app对象挂载(渲染)到指定的dom元素上 app.mount('#app') </script>
2.8.4.3、beforeMount

类型:Function

详细:在挂载开始之前被调用:相关的 render 函数首次被调用。

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

生命周期演示

页面内容,姓名:{{name}}
<script type="module"> let app = Vue.createApp({ data:function(){ return { name:"张三" } }, /* 首次调用render(渲染)函数之前调用 */ beforeMount:function(){ //$el(虚拟dom)只有在调用render之后才会被创建,所以这里还是打印null //这个函数一般用得也不算太多 console.info("--虚拟dom:",this.$el) } }); //将app对象挂载(渲染)到指定的dom元素上 app.mount('#app') </script>
2.8.4.4、mounted

类型:Function

详细:在实例挂载完成后被调用,这时候传递给 app.mount 的元素已经被新创建的 vm.$el 替换了。如果根实例被挂载到了一个文档内的元素上,当 mounted 被调用时, vm.$el 也会在文档内。 注意 mounted 不会保证所有的子组件也都被挂载完成。如果你希望等待整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick

注:vm指的是Vue自身对象,即在 data或method中的this

示例:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
  
  
      

生命周期演示

页面内容
<script type="module"> let app = Vue.createApp({ /* #app这个dom元素 挂载(渲染)完毕后调用 这时,虚拟dom($el)已被创建,可以通过虚拟dom访问html元素 但是,这个页面可能包含子组件,子组件的元素可能访问不到 */ mounted:function(){ //如果被挂载的dom只有一个直系子元素,这里打印的这个直系子元素 //但是当被挂载的dom下有多个直系子元素时(注释也包括),这里打印首个文本节点 console.info('---通过虚拟dom访问元素:',this.$el) //由于这个页面可能包含子组件,那么子组件的元素如何在这里进行访问呢? //利用 vm.$nextTick函数可解决此问题 this.$nextTick(function(){ //当所有dom(包括子组件)挂载(渲染)完毕后,将执行此函数 console.info("---此处访问子组件元素100%能访问到") }); } }); //将app对象挂载(渲染)到指定的dom元素上 //指定元素时,可使用类似jquery选择器的表达式 //其实这个被挂载的app元素,就是整个vue中的根组件 app.mount('#app') </script>
2.8.4.5、beforeUpdate

类型:Function

详细:在数据发生改变后,DOM 被更新之前被调用。这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。

示例:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

生命周期演示

页面内容
姓名:{{name}}
修改name:
<script type="module"> let app = Vue.createApp({ data:function(){ return { name:"张三" } }, /* 更改数据时触发 数据改变之后,dom渲染之前调用 */ beforeUpdate:function(){ //这里打印的name是修改过后的数据 console.info("name值:",this.name); //获取span标签中的文本内容,由于dom还未渲染,所以这个文本内容还是之前的 let text = this.$el.querySelector("span").textContent; console.info("text,",text); } }); //将app对象挂载(渲染)到指定的dom元素上 app.mount('#app') </script>
2.8.4.6、updated

类型:Function

详细:在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或侦听器取而代之。

注意,updated 不会保证所有的子组件也都被重新渲染完毕。如果你希望等待整个视图都渲染完毕,可以在 updated 内部使用 vm.$nextTick:

示例:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

生命周期演示

页面内容
姓名:{{name}}
修改name:
<script type="module"> let app = Vue.createApp({ data:function(){ return { name:"张三" } }, /* 更改数据后,当数据影响的dom渲染完毕后调用此函数 注意:如果受影响更新的有子组件,那么并不能保证子组件也渲染完毕 */ updated:function(){ //同学们记住一点就好,访问dom统一在 vm.$nextTick 中就好,因为$nextTick中100%能保证任何dom被渲染完毕 this.$nextTick(function(){ console.info("$nextTick中随便你访问子组件的dom还是其他dom,都是能100%保证是渲染过后的") }) } }); //将app对象挂载(渲染)到指定的dom元素上 app.mount('#app') </script>
2.8.4.7、beforeUnmount

类型:Function

详细:在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。

注:什么情况下会使vue组件被

示例:见unmounted

2.8.4.8、unmounted

类型:Function

详细:卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
      

生命周期演示

页面内容
姓名:{{name}}
修改name:
<script type="module"> let app = Vue.createApp({ data:function(){ return { name:"张三" } }, methods:{ //卸载Vue组件 unmountApp:function(){ //卸载 app.unmount(); } }, /* vue组件即将被卸载之前调用 那么一般什么时候会触发卸载呢? 调用 app的 unmount 函数 */ beforeUnmount:function(){ //一般这里面可以做一些清理资源的动作,比如:销毁定时器等 console.info("--卸载之前调用") }, /** * vue组件卸载完毕后调用 */ unmounted:function(){ //一般这里面可以做一些清理资源的动作,比如:销毁定时器等 console.info("--卸载后调用") } }); //将app对象挂载(渲染)到指定的dom元素上 let vm = app.mount('#app') </script>

2.9、路由

我们前面要使用组件,有以下三步操作

  • 先导入组件
  • 注册组件
  • 页面使用标签渲染组件

如果我们的页面使用的组件较少,没什么组件切换的操作倒是也不显得麻烦。但是如果我们的组件很多,或者在页面上需要频繁的切换各种组件,使用上面的操作就会显得非常麻烦

这时,我们的路由插件就有用武之地了,它可以将一个vue组件映射为一个url地址,访问这个被映射的url地址,即可渲染对应的组件

使用示例:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
  <script src="./js/vue-router-4.0.12.global.js"></script>
  
      

路由示例

导航Home组件,并渲染它
导航Update组件,并渲染它
导航Home组件-params参数
导航Home组件-query参数

<script type="module"> //导入首页面组件 let Home = loadModule("./pages/Home.vue"); //导入修改页面组件 let Update = loadModule("./pages/Update.vue"); //路由配置 const routes = [ { path:"/home", //路由名称 name:"Home", component:Home }, { path:"/update", component:Update } ]; //创建一个路由的对象 const router = VueRouter.createRouter({ //内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。 //使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会重新加载。 history: VueRouter.createWebHashHistory(), routes, // `routes: routes` 的缩写 }); let app = Vue.createApp({ data:function(){ return { //params传递参数示例 toHomeExample1:{ //根据路由名称找组件 name:"Home", //注意:传递params参数必须指定name属性,否则参数传递不过去 params:{id:'123'} }, //query传递参数示例 toHomeExample2:{ //根据路径找组件 path:"/home", query:{sex:'女'} } } } }); //让Vue应用支持路由这个插件 app.use(router); //将app对象挂载(渲染)到指定的dom元素上 let vm = app.mount('#app') </script>

2.9.1、嵌套路由

示例:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  
    
  
  
  
  <script src="./js/vue-3.2.4.global.js"></script>
  
  <script src="./js/vue3-sfc-loader.js"></script>
  
  <script src="./js/vue3-loader-helper.js"></script>
  
  <script src="./js/vue-router-4.0.12.global.js"></script>
  
      

路由示例

查看用户银行账户信息
查看用户个人基本信息

<script type="module"> //导入用户组件 let User = loadModule("./pages/User.vue"); //导入用户卡片信息组件 let UserCard = loadModule("./pages/UserCard.vue"); //导入用户个人基本信息组件 let UserInfo = loadModule("./pages/UserInfo.vue"); //路由配置 const routes = [ { path:"/user", component:User, //子路由配置 children:[ { path:"card", component: UserCard }, { path:"info", component: UserInfo } ] } ]; //创建一个路由的对象 const router = VueRouter.createRouter({ //内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。 //使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会重新加载。 history: VueRouter.createWebHashHistory(), routes, // `routes: routes` 的缩写 }); let app = Vue.createApp({ }); //让Vue应用支持路由这个插件 app.use(router); //将app对象挂载(渲染)到指定的dom元素上 let vm = app.mount('#app') </script>

2.10、axios

Vue 版本推荐使用 axios 来完成 ajax 请求。

Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中

使用格式如下:

特别注意:axios作为请求主体发送的数据(config中的data)

默认是会将js对象转换为json字符串传递给服务端的

所以如果默认配置下,config中传递了data参数,后端需要获取请求体中json串并对其进行解析。(如果后端使用了springMVC或spring boot框架,则记得使用 @ResponseBody注解即可)

//创建一个axios实例
let service = Axios.create();
/**
get请求,返回Promise对象
url 为请求地址,config为配置对象
*/
service.get(url,config)
    .then(function(resp){...})
	.catch(function(err){...})
//post请求
service.post(url,config)
    .then(function(resp){...})
	.catch(function(err){...})
//put请求
service.put(url,config)
     .then(function(resp){...})
	.catch(function(err){...})
//delete请求
service.delete(url,config)
     .then(function(resp){...})
	.catch(function(err){...})

config对象常用参数说明如下:

{
    //如果你需要在url拼接参数,可加此属性
    params:{key1:"key1",key2:"key2"},

    // `data` 是作为请求主体被发送的数据
    // 只适用于这些请求方法 "PUT", "POST", 和 "PATCH"
    data: {
      key1:"key1",
      key2:"key2"
    },

    // `headers` 是即将被发送的自定义请求头,比如下面设置请求内容类型为传统表单的形式
    headers: {"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"},
}

如果需要使用Axios跨域请求能传递cookie,则需要如下设置

//解决跨域请求cookie不能传递问题
axios.defaults.withCredentials = true;

示例:

有3个接口:

接口1:根据用户id能获取用户的 身份证号码

接口2:根据身份证号码获取绑定的银行卡号

接收3:根据银行卡号获取卡上余额

现有一用户ID,要求获取该用户的余额

2.11、Promise

解决以前深陷回调的地狱问题

axios是基于Promise的http库,能配合promise解决回调地狱问题

我们先来看看promise的基本用法,在来解决上面例子的回调地狱问题

说明:

Promise对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

  • pending: 初始状态,不是成功或失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

创建一个Promise对象

var promise = new Promise(function(resolve, reject) {
    // 对象一旦被创建,就会异步的执行此函数
    //此时,Promise对象为 pending状态
    
    //此处你可以发送ajax请求或做别的事情
    /*
    比如:我发送一个ajax请求
    axios.get('url')
        .then(function(resp){
            //处理成功,将此 Promise对象修改为fulfilled状态
            resolve(resp);
        })
        .catch(function(err){
        	//处理出错,将此 Promise对象修改为rejected状态
        	reject(err);
        })
    */
});
//then链式
promise.then(function(data){
    //如果这个then中无返回值或返回undefined,那么下一个then的data就是undefined
    //如果这个then中返回 非Promise  的对象或数据,那么下一个then的data就是这里返回的 对象或数据
    //如果这个then中返回 Promise 对象,那么下一个then的data就是 Promise对象中的 resolve回调的数据 
}).then(function(data){
    
    
})...;

示例一个简单的链式操作:

将前面axios的例子封装为Promise链式操作:

二、NPM方式构建VUE应用

npm 是Node 的模块管理器,功能极其强大。

它是Node 获得成功的重要原因之一。

正因为有了npm,我们只要一行命令,就能安装别人写好的模块.

实际上我们工作中常用vue构建项目,其做为一个独立的前端项目(前后端分离),这时候我们不但要创建一个完美的目录结构,在vue中可能还要使用到各类第三方工具或组件(如:webpack...等)。这时候我们就需要使用npm方式来安装vue了。

npm 是Node 的模块管理器,功能极其强大。我们可以利用它快速完成vue的相关组件安装。

1、关于webpack

我们接下来学习的vue是使用webpack来打包,所以我们先了解一下webpack是啥玩意儿

1.1、什么是Webpack?

本质上,webpack是一个现代JS应用程序的静态模块打包器(module bundle)。

当webpack处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle(前端资源,如:css、图片、json、js...)。

1.2、为什要使用WebPack?

webpack通过让JS来处理依赖关系和加载顺序,而非通过开发者的大脑。

webpack可纯粹的在服务端运行来构建渐进增强式的应用。

webpack试图通过提出一个大胆的想法来减轻开发者的负担:如果开发过程一部分可以自动处理依赖关系会怎样呢?若可以简单地写代码,让构建过程最终只根据需求管理自己会怎样呢?

webpack的方式是:若webpack了解依赖关系,它会捆绑(打包)在生产环境中实际需要的部分。

  • WebPack特性

将依赖树拆分保证按需加载

保证初始加载的速度

所有静态资源可被模块化

整合构建第三方库和模块

适合构建大型SPA(单页应用程序)

场景1

A同学开发了模块a,B同学开发了模块b,在页面下通过如下方式进行引用

<script src="a.js"></script>
<script src="b.js"></script>

这时模块a,模板b中的代码都暴露在全局环境中,如果模块a中定义了一个方法del。同学b并不知道,在模块b中也定义了一个方法del。这时便造成了命名冲突的的问题。如图

场景2

C同学开发了一个公共的工具库utils.js,D同学开发了一个公共的组件tab.js,tab.js依赖utils.js。

同学E需要使用D同学开发的tab.js,就需要通过如下方式引用

<script src="util.js"></script>
<script src="tab.js"></script>

同学E自己也开发了一个dailog.js同时它也依赖util.js。现在页面同时引用了dialog.js和tab.js,代码如下

<script src="util.js"></script>
<script src="dialog.js"></script>
</script>

同学E不仅需要同时引用这三个js文件,还必须保证文件之间的引用顺序是正确的。同时,从上面的代码我们无法直接看出模块与模块之间的依赖关系,如果不深入tab.js,我们无法知道tab.js到底是只依赖util.js还是dialog.js或者两者都依赖。随着项目逐渐增大,不同模块之间的依赖关系则会变的越来越难以维护也会导致许多模块中大量的变量都暴露在全局环境中。

为了解决上述这种恶心人的问题,通过使用webpack工具来打包你的前端代码,让其帮你分析处理依赖关系、 避免命名污染等一系列问题。

2、环境搭建

2.1、安装node.js

  • 官网:https://nodejs.org/en/download/current/
  • 安装课件提供的node js:node-v16.13.0-x64.msi 文件
  • 检查node js是否安装成功
    • win + r以管理员身份进入命令行窗口,键入一下命令可得知 node js是否安装成功
#查看node js版本
node -v
#查看npm管理器版本
npm -v

2.2、使用npm或cnpm安装一下模块

2.2.1、cnpm模块

npm模块管理器安装其他模块时,一般连接的国外的服务器下载模块文件进行安装,由于国内访问国外网络可能不好,容易出现下载速度慢或下载超时情况

我们可以安装cnpm来替代npm模块管理器,cnpm连接的是国内淘宝镜像下载文件,速度较快,推荐使用

# npm install 表示调用npm命令的install(安装)功能  ,来安装cnpm模块 ,-g表示全局安装
npm install cnpm -g

2.2.2、安装vue模块

cnpm install vue -g

2.2.3、安装vue-cli(脚手架构建工具)

它是一个专门为单页面应用快速搭建繁杂的脚手架

它可以轻松的创建新的应用程序而且可用于自动生成vue和webpack的项目模板。

cnpm install vue-cli -g

2.3、使用vue-cli(脚手架构建工具)构建一个项目

vue-cli构建的项目内置webpack打包工具,它是利用webpack工具来对项目打包

  • 创建项目
vue create 项目名称

Babel是编写下一代 JavaScript 的编译器

ESLint 是一个插件化并且可配置的 JavaScript 语法规则和代码风格的检查工具

  • 运行项目
#进入你刚刚创建的项目目录
cd 你的项目目录
#使用npm或cnpm都可以,运行它
cnpm run serve

2.3、目录说明

使用idea 打开我们构建的基于webpack模板的项目,在此之前

idea 安装vue插件,该插件让idea 支持开发vue项目

settings -> plugins ->搜索vue 进行安装

  • 打开vue项目,目录说明如下
目录/文件 说明
build 项目构建(webpack)相关代码
config 配置目录,包括端口号等。我们初学可以使用默认的。
node_modules npm 加载的项目依赖模块
src 这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:assets: 放置一些图片,如logo等。components: 目录里面放了一个组件文件,可以不用。App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。main.js: 项目的核心文件。index.css: 样式文件。
static 静态资源目录,如图片、字体等。
public 公共资源目录。
test 初始测试目录,可删除
.xxxx文件 这些是一些配置文件,包括语法配置,git配置等。
index.html 首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
package.json 项目配置文件。
README.md 项目的说明文档,markdown 格式
dist 使用 npm run build 命令打包后会生成该目录。
  • 利用idea工具来运行vue项目

add/edit configurations -> +号 -> npm -> scripts框框填写:dev -> apply(应用)

2.4、使用Idea开发vue项目

我们编写脚本的时候,可能会由于校验过于严格导致各种编译报错,这对于新手而言是不太友好,

关闭ESlint校验方式如下:

  • 方式一:
  • 修改 build目录 webpack.base.conf.js 文件,找到 createLintingRule() ,将其删除或注释掉,重启idea即可
  • 方式二:(建议使用此法)
  • 修改config目录下的index.js 文件,找到useEslint参数,修改为false

Idea中vue文件eslint校验 各种红色波浪线处理:

  • settings->搜索eslint,找到Eslint选项选择它 -> 右侧窗口选择Disable ESlint

介绍 浏览器 -> (main.js -> app.vue) -> index.html 流程,然后编写一个例子

2.5、vue中其他常用模块

2.5.1、路由

  • 安装
cnpm install vue-router@4
  • 使用

2.5.2、axios

  • 安装
cnpm install axios
  • 使用

相关