1、Vue

Soc:关注度分离原则
HTML+CSS+JS:视图:给用户看,刷新后台数据

  • 网络通信:axios

  • 页面跳转:vue-router

  • 状态管理:vuex

  • Vue-UI :ICE

  • Vue : 一款渐进式JavaScript框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开 发、路由、状态管理等新特性。其特点是综合了Angular (模块化)和React (虚拟DOM)的 优点;

  • Axios :前端通信框架;因为Vue 的边界很明确,就是为了处理DOM,所以并不且备通信能力,此时就需要额外使用一个通信框架与服务器交互:当然也可以直接选择使用jQuery提供的 AJAX通信功能;
    在这里插入图片描述

  • Vm:数据双向绑定

  • 虚拟Dom:利用内存

  • 计算机属性—>Vue特色

  • 集大成者:MVVM+Dom
    前端为主的MV*时代

  • 此处的MV*模式如下:

  • MVC (同步通信为主) : Model.、View、Controller

  • MVP (异步通信为主) :Model、View、Presenter

  • MVVM (异步通信为主) : Model、 View、ViewModel

  • 为了降低前端开发复杂度涌坝了大量的前端框架,比如: AngularJs、React、Vue. js、
    EmberJS等,这些框架总的原则异先按类型分层,比如Templates、Controllers、 Models, 然后再在层内做切分,如下图:
    在这里插入图片描述

优点

  • 前后端职责很清晰: 前端I作在浏览器端,后端工作在服务端。清晰的分工,可以让开发并行,测
    试数据的模拟不难,前端可以本地开发。后端则可以专注于业务逻辑的处理,输出RESTful等接
    口。
  • 前端开发的复杂度可控:前端代码很重, 但合理的分层,让前端代码能各司其职。这一块蛮有意思
    的,简单如模板特性的选择,就有很多很多讲究。并非越强大越好,限制什么,留下哪些自由,代
    码应该如何组织,所有这-切设计,得花一本书的厚度去说明。
  • 部署相对独立:可以快速改进产品体验

缺点

  • 代码不能复用。比如后端依旧需要对数据做各种校验,校验逻辑无法复用浏览器端的代码。如果可
    以复用,那么后端的数据校验可以相对简单化。
  • 全异步,对SEO不利。往往还需要服务端做同步渲染的降级方案。
  • 性能并非最佳,特别是移动互联网环境下。
  • SPA不能满足所有需求,依旧存在大量多页面应用。URL Design需要后端配合,前端无法完全
    掌控。

2、什么是MVVM

  • MVVM (Model-View-ViewModel) 是一种软件架构设计模式,由微软WPF (用于替代WinForm,以前就是用这个技术开发桌面应用程序的)和Silverlight (类似于Java Applet,简单点说就是在浏览器上运行的WPF)的架构师Ken Cooper和Ted Peters开发,是一种简化用户界面的事件驱动编程方式。由John Gossman (同样也是WPF和Silverlight的架构师)于2005年在他的博客上发表。

  • MVVM源自于经典的MVC (ModN-View -Controller)模式。MVVM的核心是ViewModel
    层,负责转换Model中的数据对象来让数据变得更容易管理和使用,其作用如下:

    • 该层向上与视图层进行双向数据绑定
    • 向下与Model层通过接口请求进行数据交互

在这里插入图片描述

3、为什么要使用MVVM

MVVM模式和MVC模式一样,主要目的是分离视图(View) 和模型(Model) ,有几大好处

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

View层展现的不是Model 层的数据,而是ViewModel 的数据,由ViewModel 负责与Model层交互,这就完全解耦了View层和Model层,这个解耦是至关重要的,它是前后端分离方案实施的
重要一环。

4、 MVVM模式的实现者

  • Model:模型层,在这里表示JavaScript对象

  • View:视图层,在这里表示DOM (HTML操作的元素)

  • ViewModel: 连接视图和数据的中间件,Vue.js就是MVVM中的ViewModel层的实现者

    在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者

  • ViewModel能够观察到数据的变化,并对视图对应的内容进行更新

  • ViewModel能够监听到视图的变化,并能够通知数据发生改变

至此,我们就明白了,Vue.js 就是一个MVVM的实现者,他的核心就是实现了DOM监听与数
据绑定
在这里插入图片描述

5、第一个Vue程序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="first">
    {{message}}
</div>
  <script>
      var vm = new Vue({
        el:"#first",
        data: {
            message: "hello,vue"
        }
      });
  </script>


</body>
</html>

6、Vue常用7大属性

  1. el属性: 用来指示vue编译器从什么地方开始解析 vue的语法,可以说是一个占位符。

<div id="first">
    <span V-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
 </span>
 </div>
 <script>
      var vm = new Vue({
        el:"#first",
        data: {
            message: "hello,vue"
        }
      });
  </script>
  1. data属性用来组织从view中抽象出来的属性,可以说将视图的数据抽象出来存放在data中。
<script>
      var vm = new Vue({
        el:"#first",
        data: {
            message: "hello,vue"
        }
      });
  </script>
  1. template属性用来设置模板,会替换页面元素,包括占位符。
  2. methods属性: 放置页面中的业务逻辑,js方法一般都放置在methods中
  3. render属性 创建真正的Virtual Dom
  4. computed属性 用来计算
  5. watch属性:
    watch:function(new,old){}
    监听data中数据的变化
    两个参数,一个返回新值,一个返回旧值

7、Vue基础语法

插值

文本

数据绑定最常见的形式就是使用 {{…}}(双大括号)的文本插值:

<div id="app">
  <p>{{ message }}</p>
</div>
HTML

使用 v-html 指令用于输出 html 代码:

<div id="app">
    <div v-html="message"></div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: '<h1>菜鸟教程</h1>'
  }
})
</script>
属性

HTML 属性中的值应使用 v-bind 指令。
以下实例判断 use 的值,如果为 true 使用 class1 类的样式,否则不使用该类:

<div id="app">
  <label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
  <br><br>
  <div v-bind:class="{'class1': use}">
    v-bind:class 指令
  </div>
</div>
    
<script>
new Vue({
    el: '#app',
  data:{
      use: false
  }
});
</script>

循环语句

if else
<!DOCTYPE html>
<html lang="en" xmlns:V-bind="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="first">
    <h1 v-if="type==='A'" >A</h1>
    <h1 v-else-if = "type==='B'">B</h1>
    <h1 v-else = "type==='C'">C</h1>
</div>
  <script>
      var vm = new Vue({
        el:"#first",
        data: {
           type:'A'
        }
      });
  </script>
</body>
</html>
for循环
<!DOCTYPE html>
<html lang="en" xmlns:V-bind="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="first">
    <!--index数组下标-->
    <!--v-for = "item in items"-->
    <li v-for = "(item,index) in items">
      {{item.message}}---{{index}}
    </li>
</div>
<script>
      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
           // js数组定义[]
            //js对象定义{}
           items: [
               {message: '小傲的一天'},
               {message: '小傲的一生'}
           ]
        }
      });
  </script>
</body>
</html>

Vue绑定事件

用v-on 指令监听DOM事件,并在触发时运行一些JavaScript代码。

<!DOCTYPE html>
<html lang="en" xmlns:V-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="first">
    <button v-on:click="LianXi">点我</button>

</div>
<script>
      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
          message: '小傲的一天'},
        methods: {
             //方法必须定义在Vue的Methods对象中, v-on:事件
            LianXi:function (event) {
               alert(this.message)
            }
          }
      });
  </script>
</body>
</html>

8、Vue:表单双绑、组件

表单双绑

什么是双向数据绑定
  • Vue.js是-个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js的精髓之处了。

  • 值得注意的是,我们所说的数据双向绑定,一定是对于 UI控件来说的,非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vuex .那么数据流也是单项的,这时就会和双向数据绑定有冲突。

为什么要实现数据的双向绑定
  • 在Vue.js 中,如果使用vuex ,实际上数据还是单向的,之所以说是数据双向绑定,这是用的UI控件来说,对于我们处理表单,Vue.js的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。
在表单中使用双向数据绑定
  • 你可以用v-model指令在表单input 、textarea 及select 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特 殊处理。
    **注意: v-model会忽略所有表单元素的value. checked. selected特性的初始值而总是将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data 选项中声明初始值!
    **
    单行文本
<!DOCTYPE html>
<html lang="en" xmlns:V-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="first">
    请输入:<input type="text" value="xiaoao" v-model="message"/>
    输入的值为:{{message}}

</div>
<script>
      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
          message: '小傲的一天' 
              }
      });

  </script>
</body>
</html>

多行文本

<div id="first">
   <textarea v-model="message">
   </textarea>
    值为{{message}}
</div>
<script>
      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
          message: '小傲的一天'
              }
      });
  </script>

单复选框

<div id="first">
     <input type="radio" name="sex" value="" v-model="checked"><input type="radio" name="sex" value="" v-model="checked">女
     性别为:{{checked}}
</div>
<script>
      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
          checked: ""
              }
      });
  </script>

多复选框

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
<script>
var vm = new Vue({
  el: '...',
  data: {
    checkedNames: []
  }
})
</script>

单选按钮

<div id="example-4">
  <input type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  <br>
  <input type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  <br>
  <span>Picked: {{ picked }}</span>
</div>
new Vue({
  el: '#example-4',
  data: {
    picked: ''
  }
})

下拉框

<div id="example-5">
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>
<script>
new Vue({
  el: '...',
  data: {
    selected: ''
  }
})
</script>

注意:如果 v-model 表达式的初始值未能匹配任何选项, 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

组件

什么是组件

组件是可复用的Vue 实例,说白了就是一组可以重复使用的模板,跟JSTL的自定义标签、Thymeleaf的th:fragment 等框架有着异曲同工之妙。通常-个应用会以一棵嵌会的组件树的形式来组织:
在这里插入图片描述
在这里插入图片描述

说明:

  • Vue.component:注册组件
  • my-component-li: 自定义组件的名字
  • template: 组件的模板
    使用props属性传递参数
    像上面那样用组件没有任何意义,所以我们是需要传递参数到组件的,此时就需要使用
    props属性了!
    注意:默认规则下props属性里的值不能为大写;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--view层 模板-->
<div id="app">
    <qinjiang v-for="item in items" v-bind:qin="item"></qinjiang>
</div>
</body>

<!--导入js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
    Vue.component("qinjiang",{
        props: ['qin'],
        template: '<li>{{qin}}</li>'
    })
    var vm = new Vue({
        el: "#app",
        data: {
            items: ['Java','Python','Php']
        }
    })
</script>
</html>

说明:

  • v- for=“item in items” :遍历Vue 实例中定义的名为items 的数组,并创建同等数量的组件
  • v-bind:item-“item”: 将遍历的item项绑定到组件中props 定义的名为item 属
    性上; =号左边的item为props定义的属性名,右边的为item in items 中遍历的
    item项的值

9、Vue:Axios异步通信

什么是Axios

Axios是一个开源的可以用在浏览器端和NodeJS 的异步通信框架,她的主要作用就是实现AJAX异步通信,其功能特点如下:

  • 从浏览器中创建XMLHttpRequests
  • 从node.js创建http请求
  • 支持Promise API [JS中链式编程]
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防御XSRF (跨站请求伪造)
    GitHub: https://github.com/ axios/axios
    中文文档: http://www.axios

Vue生命周期

  • Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、_编译模板、挂载DOM、渲染→更新-→渲染、卸载等一系列过程, 我们称这是Vue的生命周期。通俗说就是Vue实例从创破到销毁的过程,就是生命周期。
  • 在Vue的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册JS方法,可以让我们用自己注册的JS方法控制整个大局,在这些事件响应方法中的this直接指向的是Vue的实例。
  • 在这里插入图片描述

第一个Axios程序

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--view层模板-->
<div id="app">
    <div>{{info.name}}</div>
    <div>{{info.address.city}}</div>
    <a v-bind:href="info.url">点我</a>
</div>
</body>
<!--导入js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var vm = new Vue({
            el: "#app",
            data(){
                return{
                    info:{
                    name:null,
                    url:null,
                    address:{
                        street:null,
                        city:null
                    },
                    links:[
                        {
                            name:null,
                            url:null
                        }
                    ]
                }
            }
            },
            mounted(){
                axios.get("../data.json").then(response=>(this.info=response.data))
            }
        })
</script>
</body>
</html>

解释
mounted

  • 类型:Function

  • 详细:
    在这里插入图片描述

<script>
mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}
</script>

注:该钩子在服务器端渲染期间不被调用。

10、什么是计算属性?(VUE相比Angular和React的特性)

计算属性的重点突出在属性两个字上(属性是名词),首先它是个属性其次这个属性有计算的能力(计算是动词),这里的计算就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存!
例:

<!DOCTYPE html>
<html lang="en" xmlns:V-bind="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="first">
    <p> currentTime1 {{ currenttime1()}}</p>
    <p> currentTime1 {{ currenttime2}}</p>
</div>
<script>
      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
         message:"小傲"
        },
        methods:{
            currenttime1:function(){
               return Date.now();//输出当前时间戳
            },
        } ,
        computed:{
            currenttime2:function(){
               return Date.now();//输出当前时间戳
            }
        }
      });
  </script>
</body>
</html>

注意: methods和computed里的东西不能重名
说明:

  • methods:定义方法,调用方法使用currentTime1(). 需要带括号
  • computed:定义计算属性,调用属性使用currentTime2, 不需要带括号; this.message是为了能够让currentTime2观察到数据变化而变化
  • 如何在方法中的值发生了变化,则缓存就会刷新!可以在控制台使用vm. message= “qinjiang” ,改变下数据的值,再次测试观察效果!
  • **结论:**调用方法时,每次都需要进行计算K既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考思将这个结果缓存起来,采用计算属性可以很方便的做到这一点计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销;

11、内容分发

  • 在Vue.js 中我们使用 元素作为承载分发内容的出口,作者称其为插槽,可以应用在组.合组件的场景中;
  • 测试
    比如准备制作一个待办事项组件(todo) ,该组件由待办标题(todo-title) 和待办内容(todo-items)组成,但这三个组件又是相互独立的,该如何操作呢?
<!DOCTYPE html>
<html lang="en" xmlns:V-bind="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
<div id="first">
    <todo>
        <todo_title slot="todo_title" v-bind:title="title"></todo_title>
        <todo_items slot="todo_items" v-for="item in todoItems" v-bind:item="item"></todo_items>
    </todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
    Vue.component("todo",{
         template:'<div>\
                 <slot name="todo_title"></slot>\
                  <ul >\
                 <slot name="todo_items"></slot> \
                 </ul>\
          </div>'
    })
    Vue.component("todo_title",{
        props:['title'],
        template: '<div>{{title}}</div>'
    })
    Vue.component("todo_items",{
        props:['item'],
        template:'<li>{{item}}</li>'
    })

      var vm = new Vue({
        //  el 元素对象
        el:"#first",
        //  数据
        data: {
            title:"小傲学java",
            todoItems:['JAVASE','JAVAEE','JAVAME']
        },
      });
  </script>
</body>
</html>

12、自定义事件

  • 通过以上代码不难发现,数据项在Vue的实例中,但删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递事件分发了,Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题;使用this.$emit(’ 自定义事件名’,参数),操作过程如下:
  • 1-在vue的实例中,增加了methods对象并定义了一个名为removeTodoltems 的方法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!--1.导入vue.js-->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
    <todo>
        <todo-title slot="todo-title" v-bind:title="title"></todo-title>
        <todo-items slot="todo-items"
                    v-for="(item,index) in todoItems"
                    v-bind:item="item" v-bind:index="index"
                    v-on:remove="removeItem(index)">
        </todo-items>
    </todo>
</div>
<script>
    //slot 插槽 这个组件要定义在前面不然出不来数据
    Vue.component("todo", {
        template: '<div>\
                <slot name="todo-title"></slot>\
                <ul>\
                <slot name="todo-items"></slot>\
                </ul>\
                <div>'
    });
    Vue.component("todo-title", {
        //属性
        props: ['title'],
        template: '<div>{{title}}</div>'
    });
    Vue.component("todo-items", {
        props: ['item', 'index'],
        template: '<li>{{index}}-{{item}}<button style="margin: 5px" @click="remove">删除</button></li>',
        methods: {
            remove: function (index) {
                // this.$emit('事件',参数) 自定义事件分发(远程调用方法)
                this.$emit('remove', index)
            }
        },
    });
    let vm = new Vue({
        el: "#app",
        data: {
            //标题
            title: "小傲学java",
            //列表
            todoItems: ['JAVASE', 'JAVAEE', 'JAVAME']
        },
        methods: {
            removeItem: function (index) {
                // 一次删除一个元素
                this.todoItems.splice(index, 1)
                console.log("删除了" + this.todoItems[index] + "OK")
            }
        },
    });
</script>
</body>
</html>

13、Vue入门小结

  • 核心:数据驱动,组件化
  • 优点:借鉴了AngulaJS的模块化开发和React的虚拟Dom ,虛拟Dom就是把Dom操作放到内存中执行; .
    • 常用的属性:
      • v-if
      • v-else-if
      • v-else
      • v-for
      • v-on绑定事件,简写@
      • v-model数据双向绑定
      • v-bind给组件绑定参数,简写:
    • 组件化:
      • 组合组件slot插槽
      • 组件内部绑定事件需要使用到this. $emit(“事件名”,参数) ;
      • 计算属性的特色,缓存计算数据

遵循SoC关注度分离原则,Vue是纯粹的视图框架,并不包含,比如Ajax之类的通信功能,为了解信问题,我们需要使用Axios框架做异步通信;

14、Vue:第一个vue-cli项目

什么是vue-cli

  • vue-cli官方提供的一个脚手架用于快速生成一-个vue的项目模板;

    预先定义好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速;

    主要的功能:

    • 统一的目录结构
    • 本地调试
    • 热部署
    • 单元测试
    • 集成打包.上线

vue需要的环境

  1. Node.js : http://nodejs.cn/download/
    安装就无脑下一步就好,安装在自己的环境目录下

  2. Git : https://git-scm.com/downloads
    镜像:https://npm.taobao.org/mirrors/git-for-windows/

  3. 确认nodejs安装成功:

    • cmd 下输入 node -v,查看是否能够正确打印出版本号即可!
    • cmd 下输入 npm-v,查看是否能够正确打印出版本号即可!

    这个npm,就是一个软件包管理工具,就和linux下的apt软件安装差不多!
    安装 Node.js 淘宝镜像加速器(cnpm)
    这样子的话,下载会快很多~
    在命令台输入

# -g 就是全局安装
npm install cnpm -g
# 或使用如下语句解决 npm 速度慢的问题
npm install --registry=https://registry.npm.taobao.org
  • 安装的位置:C:\Users\Administrator\AppData\Roaming\npm
    在这里插入图片描述

安装vue-cli

#在命令台输入
cnpm install vue-cli -g
#查看是否安装成功
vue list

在这里插入图片描述

在这里插入图片描述

第一个 vue-cli 应用程序

  • 创建一个Vue项目,我们随便建立一个空的文件夹在电脑上,我这里在D盘下新建一个目录D:\代码\myvue;创建一个基于 webpack 模板的 vue 应用程序
# 这里的 myvue 是项目名称,可以根据自己的需求起名
vue init webpack myvue

一路都选择no即可;
初始化并运行
在这里插入图片描述

cd myvue
npm install
npm run dev

cmd运行成功

在这里插入图片描述浏览器访问localhost:8080 运行成功页面
在这里插入图片描述

15、Vue:Webpack学习

什么是Webpack

  • 本质上, webpack是- -个现代JavaScript应用程序的静态模块打包器(module bundler)。当webpack处理应用程序时,它会递归地构建:个依赖关系 (dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle.
  • Webpack是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过loader转换,任何形式的资源都可以当做模块,比如CommonsJS.AMD、ES6、CSS. JSON、CoffeeScript、 LESS 等;
  • 伴随着移动互联网的大潮,当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里,使用HTML5、CSS3、ES6 等新的技术来开发丰富的功能,网页已经不仅仅是完成浏览器的基本需求; WebApp通常是- -个SPA (单页面应用) ,每一个视图通过异步的方式加.载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。
  • 前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

模块化演进

  • Script标签
<script src="module1.js"></scirpt>
<script src="module2.js"></scirpt>
<script src="module3.js"></scirpt>
<script src="modu1e4.js"></scirpt>

这是最原始的JavaScript文件加载方式,如果把每- -个文件看做是一一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在window时象中,不同模块的调用都是一个作用域。这种原始的加载方式暴露了一些显而易见的弊端:

  • 全局作用域下容易造成变量冲突
  • 文件只能按照script的书写顺序进行加载
  • 开发人员必须主观解决模块和代码库的依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

CommonsJS

  • 服务器端的NodeJS遵循CommonsJS规范,该规范核心思想是允许模块通过requile方法来同步加载所需依赖的其它模块,然后通过exports或module.exports来导出需要暴露的接口。
require("module");
require(". ./modu1e.js");
export.doStuff = function() {};
module.exports = someValue;

优点:

  • 服务器端模块便于重用
  • NPM中已经有超过45万个可以使用的模块包
  • 简单易用

缺点:

  • 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

实现:

  • 服务端的NodeJS
  • Browserify, 浏览器端的CommonsJS实现,可以使用NPM的模块,但是编译打包后的文件体积较大
  • modules-webmake, 类似Browserify,但不如Browserify灵活
  • wreq, Browserify的前身

AMD

  • Asynchronous Module Definition规范其实主要-一个主要接口define(id?,
    dependencies?, factory);它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行。
define("module",["dep1", "dep2"], function(d1, d2) {
return someExportedValue ;
});
require(["modu1e", ". ./file.js"],function(module, file) {});

优点

  • 适合在浏览器环境中异步加载模块
  • 可以并行加载多个模块日

缺点

  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
  • 不符合通用的模块化思维方式,是一种妥协的实现,

实现.

  • RequireJS .
  • curl

ES6模块

  • EcmaScript6标准增加了JavaScript语言层面的模块体系定义。ES6 模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonsJS 和AMD模块,都只能在运行时确定这些东西。
import "jquery";
export function doStuff() {}
module "localModule" {}

优点

  • 容易进行静态分析
  • 面向未来的EcmaScript标准

缺点

  • 原生浏览器端还没有实现该标准
  • 全新的命令,新版的NodeJS才支持
    实现.
  • Babel

安装Webpack

WebPack是-款模块加载器兼打包工具,它能把各种资源,如JS. JSX、ES6、SASS、LESS.图片等都作为模块来处理和使用。
安装:

npm install webpack -g
npm install webpack-cli -g 

测试安装成功
在这里插入图片描述
配置
创建webpack.config.js 配置文件

  • entry: 入口文件,指定WebPack用哪个文件作为项目的入口
  • output: 输出,指定WebPack把处理完成的文件放置到指定路径
  • module:模块,用于处理各种类型的文件
  • plugins:插件,如:热更新、代码重用等
  • resolve: 设置路径指向
  • watch:监听,用于设置文件改动后直接打包

使用webpack

  1. 创建项目
    在这里插入图片描述

  2. 创建一个名为modules的目录,用于放置JS模块等资源文件
    在这里插入图片描述

  3. 在modules下创建模块文件,如hello.js,用于编写JS模块相关代码

//暴露一个方法:sayHi
exports.sayHi = function () {
  document. write("<div>He11o WebPack</div>");
};
  1. 在modules下创建一一个名为 main.js 的入口文件,用于打包时设置entry 属性
//require导入一个模块,就可以调用这个模块中的方法了
var he11o = require("./he11o") ;
hello.sayHi();

在这里插入图片描述

  1. 在项目目录下创建webpack.config.js配置文件,使用webpack命令打包
 module.exports = {
	entry: "./modules/main.js",
	output: {
	filename: "./js/bund1e.js
 }
};

在这里插入图片描述

16、Vue: vue-router路由

说明
< 学习的时候,尽量的打开官方的文档 >
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于Vue.js过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的CSS class的链接
  • HTML5历史模式或hash模式,在IE9中自动降级
  • 自定义的滚动条行为

安装
基于第一个vue-cli进行测试学习;先查看node_ modules中是否存在vue-router
vue-router是一个插件包,所以我们还是需要用npm/cnpm来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。

npm install vue-router --save-dev 

如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能:

import Vue from
import VueRouter from ' vue-router
Vue.use(VueRouter);

测试
vue-router demo实例
//基于之前得到vue-cli空项目
1.将之前案例由vue-cli生成的案例用idea打开
2.清理不用的东西: assert下的logo图片, component定义的helloworld组件,这里我们用自己定义的组件
3. 清理代码 以下为清理之后的代码 src下的App.vue 和main.js以及根目录的index.html
这三个文件的关系是 index.html 调用 main.js 调用 App.vue

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>myvue</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

main.js

import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
new Vue({
  el: '#app',
  //配置路由
  router,
  components: { App },
  template: '<App/>'
})

App.vue

<template>
  <div id="app">

  </div>
</template>

<script>

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

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

  1. 在components目录下创建一个自己的组件Content,Test,Main

Content.vue

<template>
    <h1>内容页</h1>
</template>

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

<style scoped>

</style>

Main.vue

<template>
    <h1>首页</h1>
</template>

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

<style scoped>

</style>

5.在src目录下新建一个router路由文件夹,在router文件夹下建一个index.js路由配置文件
index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Content from '../components/Content'
import Main from  '../components/Main'
//安装路由
Vue.use(VueRouter);
//配置导出路由
export default new VueRouter({
  routes:[
    {
      //路由的路径
      path:'/content',
      name:'/content',
     // 跳转的组件
      component:Content
    },
    {
      //路由的路径
      path:'/main',
      name:'/main',
      // 跳转的组件
      component:Main
    }
  ]
 }
)

6.在main.js下配置自动扫描路由

import Vue from 'vue'
import App from './App'
import router from './router' //自动扫描里面的路由配置
Vue.config.productionTip = false

new Vue({
  el: '#app',
  //配置路由
  router,
  components: { App },
  template: '<App/>'
})

7.在App.vue下使用路由

<template>
   <div id="app">
    <!--控制路由-->
  <router-link to="/content">内容页</router-link>
  <router-link to="/main">首页</router-link>
    <!--控制页面展示-->
  <router-view></router-view>
  </div>
</template>

<script>

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

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

8.在idea中的Terminal(注:Terminal需要以管理员身份运行)运行使用以下指令

npm run dev

9.测试成功页面
在这里插入图片描述

17、Vue+ElementUI

我们采用实战教学模式并结合ElementUI 组件库,将所需知识点应用到实际中,以最快速度带领大家掌握Vue的使用;
创建工程
注意:命令行都要 使用管理员模式运行

  1. 创建一个名为hello-vue的工程 vue init webpack hello-vue
  2. 安装依赖,我们需要安装vue-router、element-ui、 sass -loader和node-sass四个插件
    #进入工程目录
cd hello-vue
#安装vue-router
npm install vue-router --save-dev
#安装element-ui
npm i element-ui -S
#安装依赖
npm install
#安装SASS加载器
cnpm install sass-loader node-sass --save-dev
#启动测试
npm run dev
  1. Npm命令解释:
  • npm install moduleName :安装模块到项目目录下
  • npm install -g moduleName : -g的意思是将模块安装到全局,具体安装到磁盘哪个位置,
    要看npm config prefix 的位置
  • npm install -save moduleName : --save 的意思是将模块安装到项目目录下,并在
  • package文件的dependencies节点写入依赖,-S为该命令的缩写
  • npm install -save-dev moduleNam e: --save-dev的意思是将模块安装到项目目录下,并
    在package文件的devDependencies节点写入依赖,-D为该命令的缩写
  1. 创建成功后用idea打开,并删除净东西 创建views和router文件夹用来存放视图和路由
    在这里插入图片描述
  2. 在views目录下创建Login.vue
    Login.vue(这里采用ElementUI)
<template>
  <div>
    <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
      <h3 class="login-title">欢迎登录</h3>
      <el-form-item label="账号" prop="username">
        <el-input type="text" placeholder="请输入账号" v-model="form.username"/>
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input type="password" placeholder="请输入密码" v-model="form.password"/>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
      </el-form-item>
    </el-form>

    <el-dialog
      title="温馨提示"
      :visible.sync="dialogVisible"
      width="30%"
      :before-close="handleClose">
      <span>请输入账号和密码</span>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  export default {
    name: "Login",
    data() {
      return {
        form: {
          username: '',
          password: ''
        },

        // 表单验证,需要在 el-form-item 元素中增加 prop 属性
        rules: {
          username: [
            {required: true, message: '账号不可为空', trigger: 'blur'}
          ],
          password: [
            {required: true, message: '密码不可为空', trigger: 'blur'}
          ]
        },

        // 对话框显示和隐藏
        dialogVisible: false
      }
    },
    methods: {
      onSubmit(formName) {
        // 为表单绑定验证功能
        this.$refs[formName].validate((valid) => {
          if (valid) {
            // 使用 vue-router 路由到指定页面,该方式称之为编程式导航
            this.$router.push("/main");
          } else {
            this.dialogVisible = true;
            return false;
          }
        });
      }
    }
  }
</script>

<style lang="scss" scoped>
  .login-box {
    border: 1px solid #DCDFE6;
    width: 350px;
    margin: 180px auto;
    padding: 35px 35px 15px 35px;
    border-radius: 5px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    box-shadow: 0 0 25px #909399;
  }

  .login-title {
    text-align: center;
    margin: 0 auto 40px auto;
    color: #303133;
  }
</style>


  1. 在views下创建Main.vue
    Main.vue
<template>
    <h1>首页</h1>
</template>

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

<style scoped>

</style>

  1. 在router文件下创建index.js路由配置文件
    index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Main'
import Main from  '../views/Login'
Vue.use(VueRouter);

export default new VueRouter({
   routes:[{
     path:'/main',
     component: Main
   },
     {
       path:'/login',
       component:Login
     }
   ]

})

  1. 配置main.js
    main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(router);
Vue.use(Element);

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

  1. App.vue
<template>
  <div id="app">
    <router-link to="/login">登录</router-link>
    <router-link to="/main">主页</router-link>
    <router-view></router-view>
  </div>
</template>

<script>

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

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

  1. 在控制台输入 npm run dev 运行项目
    注: “sass-loader”: "^5.0.0 ",sass的版本不能太高,如果报错以下错误,应适当调低版本
    在这里插入图片描述

运行成功页面
在这里插入图片描述

18、路由嵌套

嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。同样地,URL中各段动态路径也按某种结构对应嵌套的各层组件,例如:
在这里插入图片描述
路由嵌套实例

  1. 在上一个项目基础上,在views目录下新建一个user文件夹,在user文件下创建,List.vue和perfile.vue两个文件
    在这里插入图片描述
    2.编写List.vue(perfile.vue类似)
<template>
    <h1>用户列表</h1>
</template>

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

<style scoped>

</style>

3.编写index.js,配置嵌套路由修改 router 目录下的 index.js 路由配置文件,使用children放入main中写入子模块,代码如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login'
import Main from  '../views/Main'
import UserList from '../views/user/List'
import UserPerfile from  '../views/user/Perfile'
Vue.use(VueRouter);

export default new VueRouter({
   routes:[{
     path:'/login',
     component: Login,
   },
     {
       path:'/main',
       component:Main,
       children:[{
         path:'/user/list',          //路由嵌套             
         component:UserList},
         {
           path:'/user/perfile',
           component:UserPerfile
         }
       ]
     }
   ]

})

  1. 编写Main.vue
<template>
  <div>
    <el-container>
      <el-aside width="200px">
        <el-menu :default-openeds="['1']">
          <el-submenu index="1">
            <template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
            <el-menu-item-group>
              <el-menu-item index="1-1">
                <!--插入的地方-->
                <router-link to="/user/perfile">个人信息</router-link>
              </el-menu-item>
              <el-menu-item index="1-2">
                <!--插入的地方-->
                <router-link to="/user/list">用户列表</router-link>
              </el-menu-item>
            </el-menu-item-group>
          </el-submenu>
          <el-submenu index="2">
            <template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
            <el-menu-item-group>
              <el-menu-item index="2-1">分类管理</el-menu-item>
              <el-menu-item index="2-2">内容列表</el-menu-item>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </el-aside>

      <el-container>
        <el-header style="text-align: right; font-size: 12px">
          <el-dropdown>
            <i class="el-icon-setting" style="margin-right: 15px"></i>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item>个人信息</el-dropdown-item>
              <el-dropdown-item>退出登录</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </el-header>
        <el-main>
          <!--在这里展示视图-->
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script>
  export default {
    name: "Main"
  }
</script>
<style scoped lang="scss">
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }
  .el-aside {
    color: #333;
  }
</style>

  1. 测试
    在这里插入图片描述

1、参数传递

第一种参数传值

  1. 传递参数
     此时我们在Main.vue中的route-link位置处 to 改为了 :to,是为了将这一属性当成对象使用,注意 router-link 中的 name 属性名称 一定要和 路由中的 name 属性名称 匹配,因为这样 Vue 才能找到对应的路由路径;
 <el-menu-item index="1-1">
                <!--插入的地方-->
                <router-link  v-bind:to="{name:'UserPerfile',params:{id:1}}">个人信息</router-link>
              </el-menu-item>
  1. 修改路由配置, 主要是router下的index.js中的 path 属性中增加了 :id 这样的占位符
 children:[{
         path:'/user/list',
         component:UserList},
         {
           path:'/user/perfile/:id',
           name:'UserPerfile',
           component:UserPerfile
         }

3.在要展示的组件Profile.vue中接收参数 使用 {{$route.params.id}}来接收
Profile.vue 部分代码

<template>
  <div>
    {{$route.params.id}}
    <h1>个人信息</h1>

  </div>
</template>

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

<style scoped>

</style>

第二种取值方式 使用props 减少耦合

  1. 修改路由配置 , 主要在router下的index.js中的路由属性中增加了 props: true 属性
path:'/user/perfile/:id',
           name:'UserPerfile',
           component:UserPerfile,
           props:true,
  1. 传递参数和之前一样 在Main.vue中修改route-link地址
<!--name是组件的名字 params是传的参数 如果要传参数的话就需要用v:bind:来绑定-->
<router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>
  1. 在Profile.vue接收参数为目标组件增加 props 属性
    Profile.vue
<template>
  <div>
    <!--{{$route.params.id}}-->
    <!--<h1>个人信息</h1>-->
		{{id}}
  </div>
</template>

<script>
    export default {
      props:['id'],
        name: "UserPerfile"
    }
</script>

<style scoped>

</style>

19、组件重定向

重定向的意思大家都明白,但 Vue 中的重定向是作用在路径不同但组件相同的情况下,比如:在router下面index.js的配置

 routes:[{
     path:'/login',
     component: Login,
   },{
       path:'/goHome',
       redirect:'/main'
   },

说明:这里定义了两个路径,一个是 /main ,一个是 /goHome,其中 /goHome 重定向到了 /main 路径,由此可以看出重定向不需要定义组件;

使用的话,只需要在Main.vue设置对应路径即可;

<el-menu-item index="1-3">
                <!--插入的地方-->
                <router-link to="/goHome">回到首页</router-link>
              </el-menu-item>

20、路由钩子与404

路由模式有两种
●hash:路径带#符号,如http://localhost/#/login
●history: 路径不带#符号,如http://localhost/login
修改index.js 路由配置,代码如下:

 mode:'history',
   routes:[{
     path:'/login',
     component: Login,
   }
404问题
  1. 在views目录下新建一个NotFound.vue页面
    Notfound.vue
<template>
    <h1>404,页面没有找到</h1>
</template>

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

<style scoped>

</style>

  1. 配置路由index.vue
    index.vue
  {
       path:'*',
       component:NotFound
     }

路由钩子与异步请求

beforeRouteEnter:在进入路由前执行
beforeRouteleave :在离开路由前执行

 beforeRouteEnter:(to,from,next)=>{
         console.log("进入路由之前")
      },
      //进入路由之后
      beforeRouteleave:(to,from,next)=>{
        console.log("进入路由之后")
    }

参数说明: .

  • to:路由将要跳转的路径信息
  • from:路径跳转前的路径信息
  • next: 路由的控制参数的
  • next()跳入下一个页面
  • next(’/path’)改变路由的跳转方向,使其跳到另一个路由
  • next(false) 返回原来的页面
  • next((vm)=> {})仅在beforeRouteEnter 中可用,vm是组件实例
    在钩子函数中使用异步请求
  1. 安装Axios cnpm install axios
  2. main.js引用Axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
  1. 准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放入该目录下。
    数据和之前用的json数据一样 需要的去上述axios例子里
// 静态数据存放的位置
static/mock/data.json
  1. 在 beforeRouteEnter 中进行异步请求
    Profile.vue
export default {
    //第二种取值方式
    // props:['id'],
    name: "UserProfile",
    //钩子函数 过滤器
    beforeRouteEnter: (to, from, next) => {
      //加载数据
      console.log("进入路由之前")
      next(vm => {
        //进入路由之前执行getData方法
        vm.getData()
      });
    },
    beforeRouteLeave: (to, from, next) => {
      console.log("离开路由之前")
      next();
    },
    //axios
    methods: {
      getData: function () {
        this.axios({
          method: 'get',
          url: 'http://localhost:8080/static/mock/data.json'
        }).then(function (response) {
          console.log(response)
        })
      }
    }
  }

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐