Vue入门基础

Soc:

HTML + CSS + JS :视图(给用户看,刷新后台给的数据)

网络通信:axios

页面跳转:vue - router

状态管理:vuex

Vue-UI:ICE

M:模型 V:视图 C:控制器

View:JSP{{}}

DATA:数据

vm:数据双向绑定

虚拟Dom:利用内存

计算属性--> Vue特色

集大成者:MVVM + Dom

为什么要使用MVVM?

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

  • 低耦合
  • 可复用
  • 独立开发
  • 可测试

大前端时代:后端:分布式、微服务、大数据

初始Vue

第一个Vue程序

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
    {{message}}
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            message:"hello ,vue",
        }
    });
</script>
</body>
</html>

v-bind

将这个元素节点的特性和Vue实例的data属性保持一致。

java 复制代码
<!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">
   <span v-bind:title="message">
       鼠标悬停几秒
   </span>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            message:"hello ,vue",
        }
    });
</script>
</body>
</html>

判断 循环

if
java 复制代码
<!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">
    <h1 v-if="flag">Yes</h1>
    <h1 v-else>No</h1>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            flag:true
        }
    });
</script>
</body>
</html>
v-else-if
html 复制代码
<!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">
    <h1 v-if="flag==='A'">A</h1>
    <h1 v-else-if="flag==='B'">B</h1>
    <h1 v-else>C</h1>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            flag:'A'
        }
    });
</script>
</body>
</html>
for
html 复制代码
<!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">
    <li v-for="(item,index) in item" :key="item.name">
        {{index}}-->{{item.name}}
    </li>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            item:[
                {name:"zs"},
                {anme:"ls"},
                {name:"wangwu"}
            ]
        }
    });
</script>
</body>
</html>

事件(v-on)

html 复制代码
<!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>
</head>
<body>
<!--view层 模板-->
<div id="app">
   <button v-on:click="hello">点我</button>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
           msg:"hello youyuan"
        },
        methods:{
            hello:function(){//方法必须定义在Vue的Methods对象中
                alert(this.msg);
            }
        }
    });
</script>
</body>
</html>

双向绑定

Vue.js是一个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。

单选框
html 复制代码
<!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>
</head>
<body>
<!--view层 模板-->
<div id="app">
  性别:
    <input type="radio" name="sex" value="男" v-model="youyuan">男
    <input type="radio" name="sex" value="女" v-model="youyuan">女
    <p>
        选中了:{{youyuan}}
    </p>

</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            youyuan:"",
        },

    });
</script>
</body>
</html>
下拉框
html 复制代码
<!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>
</head>
<body>
<!--view层 模板-->
<div id="app">
    下拉框:
    <select name="za" id="za" v-model="select">
        <option disabled value="">请选择</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
    </select>
    选中的值:{{select}}
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            select:"",
        },

    });
</script>
</body>
</html>

Vue组件

自定义组件

html 复制代码
<!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>
</head>
<body>
<!--view层 模板-->
<div id="app">
    <!--组件:传递给组件中的值:props-->
    <youyuan v-for="item in items" v-bind:item="item"></youyuan>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    //定义一个Vue组件Component
    Vue.component("youyuan",{
        props: ['item'],
        template: '<li>{{item}}</li>'
    });
    var vm = new  Vue({
        el:"#app",
        data:{
            items:["zs","ls","ww"]
        },
    });
</script>
</body>
</html>

Axios

JSON

json 复制代码
{
    "employees": [
        {
            "firstName": "Bill",
            "lastName": "Gates"
        },
        {
            "firstName": "George",
            "lastName": "Bush"
        },
        {
            "firstName": "Thomas",
            "lastName": "Carter"
        }
    ]
}
html 复制代码
<!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>
</head>
<body>
<!--view层 模板-->
<div id="app">
     <li v-for="(employee,index) in data.employees">
         {{employee.firstName}}-->{{employee.lastName}}
     </li>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        //data:  属性:vm
        data(){
            return{
                //请求的返回参数合适,必须和JSON字符串一样
                data:{
                    employees:[
                        {firstName:'',lastName:''},
                        {firstName:'',lastName:''},
                        {firstName:'',lastName:''},
                    ]
                },
            }
        },
        mounted(){//钩子函数 链式编程 ES6新特性
            const  _this = this;
            axios.get('data.json').then(data=>{
                _this.data = data.data;
            });
        }
    });
</script>
</body>
</html>

计算属性

计算出来的结果,保存在内存里面。调用方法时,每次都需要进行计算,既然有计算过程则必定产生开销,如果这个结果是不经常变化的,此时就可以考虑将这个结果进行缓存,以节约我们的系统开销。

html 复制代码
<!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">
    <p>{{currentTime1()}}</p>
    <p>{{currentTime2}}</p>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    var vm = new  Vue({
        el:"#app",
        data:{
            message:"hello ,vue",
        },
        methods: {
            currentTime1:function () {
                return Date.now();//返回一个时间戳
            }
        },
        computed:{   //计算机属性:methods ,computed 方法名不能重名,重名之后,只会调用methods的方法
            currentTime2:function () {
                return Date.now();
            }
        }
    });
</script>
</body>
</html>

插槽

一个组件可以放在另一个组件里面。

html 复制代码
<!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">
    <todo>
        <todo-title slot="todo-title" :title="title"></todo-title>
        <todo-title slot="todo-title" :title="title"></todo-title>
        <todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
    </todo>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>

    //slot:插槽
    Vue.component("todo",{
         template: '<div>\
                        <slot name="todo-title"></slot>\
                        <ul>\
                            <slot name="todo-items"></slot>\
                        </ul>\
                    </div>'
    });

    Vue.component("todo-items",{
        props:['item'],
        template: '<div>{{item}}</div>'
    });

    Vue.component("todo-title",{
        props:['title'],
        template: '<div>{{title}}</div>'
    });

    var vm = new  Vue({
        el:"#app",
        data:{
            title:'youyuan',
            todoItems:['youyuan1','youyuan2','youyuan3']
        }
    });
</script>
</body>
</html>

自定义事件分发

数据项在Vue实例中,但删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据,此时就涉及到了参数传递与事件分发了,Vue为我们提供了自定义事件的功能很好的解决了这个问题:使用this.$emit(''自定义事件名",参数)。

html 复制代码
<!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">
    <todo>
        <todo-title slot="todo-title" :item="title"></todo-title>
 
        <todo-items slot="todo-items" v-for="(item,index) in todoItems"
                    :index="index" :item="item" v-on:remove="remove(index)" :key="index"></todo-items>
    </todo>
</div>
<!--1. 导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<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:['item'],
        template: '<div>{{item}}</div>'
    });

    Vue.component("todo-items",{
        props:['title','index'],
        template: '<div>{{index}}--->{{title}}<button @click="remove">删除</button></div>',
        methods:{
            remove(index){
               //this.$emit 自定义事件分发
                this.$emit('remove',index);
            }
        }
    });

    var vm = new  Vue({
        el:"#app",
        data:{
            title:'youyuan',
            todoItems:['youyuan1','youyuan2','youyuan3']
        },
        methods: {
            remove(index){
                this.todoItems.splice(index,1);
            }
        }
    });
</script>
</body>
</html>

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是纯粹的视图框架。

说明:Vue的开发都是要基于Node.js,实际开发采用vue-cli脚手架开发,vue-router路由,vuex做状态管理,Vue UI界面一般使用Element来快速搭建前端项目。

Vue-cli

1、安装node.js环境

2、安装Node.js淘宝镜像加速器

3、安装vue -cli

创建Vue项目

1、vue ui

2、

3、

4、

5、

6、不保存模板。

Webpack

Webpack是一款模块加载器兼打包工具,它能把各种资源,如JS、JSX、ES、SASS图片等都作为模块来处理和使用。

安装

shell 复制代码
npm install webpack -g
npm install webpack-cli -g

测试

shell 复制代码
webpack -v
webpack -cli -v

配置

创建webpack.config.js配置文件

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

使用Webpack

1、创建项目

2、创建一个名为modules的目录,用于放置JS模块等资源文件。

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

javascript 复制代码
//暴露一个方法
exports.sayHello =  function () {
    document.write("<h1>youyuan</h1>");
}

4、在modules下创建一个名为mian.js的入口文件,用于打包时设置entrty属性。

javascript 复制代码
var hello = require("./hello");
hello.sayHello();

5、在项目目录下创建webpack.config.js配置文件,使用webpack命令打包。

javascript 复制代码
module.expotrs = {
  entry:'./modules/main.js',
  output:{
      filename:'./js/bundle.js'
  }
};

ES6模板

ES6模块的设计思想,是尽量静态化,使编译就能确定模块的依赖关系,以及输入和输出的变量。

Vue Router

路由导航

当前router的名称

javascript 复制代码
this.$router.currentRoute.name

element UI

看文档

参数传递与重定向

参数传递

name传组件名, params传递参数,需要对象,v-bind

vue 复制代码
<router-link :to="'/blog/'+ blog.id">
    <h2 v-rainbos>{{blog.title | touppercase}}</h2>
</router-link>

路由:

javascript 复制代码
{
    path:'/blog/:id',
    name:'SingleBlog',
    component:SingleBlog
}

接收:

javascript 复制代码
data(){
    return{
        id:this.$route.params.id,
    }
},

重定向

javascript 复制代码
{
    path:'/goHome',
    redirect:'/mian'
}

404和路由钩子

vue 复制代码
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'Home',
  props:['id'],
  beforeRouteEnter:(to,from,next)=>{
  },
  beforeRouteLeave:(to,from,next)=>{

  },
  components: {
    HelloWorld
  }
}
</script>
  • to:路由将要跳转的路径信息
  • from:路径跳转前的路径信息
  • next:路径的控制参数
    • next() 跳入下一个页面
    • next('/path') 改变路由的跳转方向,使其跳转到另一个路由
    • next(false) 返回原来的页面
    • next((vm)=>{}) 仅在beforeRouteEnter中可用,vm提供组件实例

自定义指令

 <div v-theme:column="'narrow'" id="show-blogs">
 </div>
javascript 复制代码
//自定义指令
Vue.directive('theme',{
  bind(el,binding,vNode){
    if (binding.value == 'wide'){
      el.style.maxWidth = '1260px';
    }else if (binding.value == 'narrow'){
      el.style.maxWidth = '560px';
    }
    if (binding.arg == 'column'){
      el.style.background = "#6677cc";
      el.style.padding = '10px';
    }
  }
})

过滤器

vue 复制代码
{{blog.context | snippet}}
javascript 复制代码
Vue.filter('snippet',function (value) {
  return value.slice(0,50)+'......';
})

局部过滤器与指令

javascript 复制代码
filters:{
    touppercase(value){
        return value.toUpperCase();
    }
},

//局部自定义组件
directives:{
   'rainbos':{
    vbind(el,binding,vNode){
        el.style.color = '#'+Math.random().toString(16).slice(2,8);
       }
   }
},

分页

vue 复制代码
<el-pagination
               background
               small
               layout="prev, pager, next"
               :page-size="pageSize"
               :total="total"
               @current-change="page" >
</el-pagination>
javascript 复制代码
created() {
    const _this = this ;
    _this.$axios.get("/blogs/1/3").then(function (data) {

        _this.blogs = data.data.tableData;
        _this.total = data.data.total;
        _this.pageSize = 3;
    });
},
    methods:{
        page(currentPage){
            const _this = this;
            _this.$axios.get('/blogs/'+(currentPage)+'/3').then(function (data) {
                console.log(data);
                _this.blogs = data.data.tableData;
                _this.total = data.data.total;
                _this.pageSize = 3;
            });
        }
    },

选择dom元素

起别名:ref = " 表单的名字 "

使用:

vue 复制代码
this.$refs["loginForm"].validate((valid) => {

}

this.

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

在整个所有vue文件都能访问,相互调用,数据共享,安全。

创建vuex:index.js

javascript 复制代码
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    /***
     * 声明变量*/
    state: {
        //存放菜单项
        routes:[],
    },

    mutations: {
        //将查询出来的菜单项赋值给state
        initRoutes(state,data){
            state.routes = data;
        }
    },

    // actions: {
    // },

    modules: {
    },
})

在main.js中导入vuex

javascript 复制代码
import store from './store'

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

请求菜单数据

javascript 复制代码
import {getRequest} from "./api";

/*菜单初始化的方法*/
export const initMenu = (router, store) => {
    /**
     * 判断是否有菜单数据
     * 如果有 直接返回
     * */
    if (store.state.routes.length > 0) {
        return;
    }

    /*获取请求*/
    getRequest("/system/config/menu").then(data => {
        if (data) {
            /*将数据中Component字符串转换为对象*/
            let fmtRoutes = formatRoutes(data);
            //放入router路由中
            router.addRoutes(fmtRoutes);
            //vuex初始化数据
            store.commit('initRoutes',fmtRoutes);
            
            //store.dispatch('connect');
        }
    })
}

export const formatRoutes = (routes) => {
    /*返回的数据*/
    let fmRoutes = [];
    /**
     * 遍历
     * p量的定义变量(赋值)*/
    routes.forEach(router => {
        let {
            path,
            component,
            name,
            meta,
            iconcls,
            children
        } = router;
        //如果有children子节点
        if (children && children instanceof Array) {
            /*递归调用*/
             children = formatRoutes(children);
        }
        let fmRouter = {
            path: path,
            name: name,
            iconcls: iconcls,
            meta: meta,
            children: children,
            /**
             * 动态导入Component组件
             * */
            component:resolve => {
                if (component.startsWith("Home")) {
                    require(['../views/' + component + '.vue'], resolve);
                } else if (component.startsWith("Emp")) {
                    require(['../views/emp/' + component + '.vue'], resolve);
                } else if (component.startsWith("Per")) {
                    require(['../views/per/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sal")) {
                    require(['../views/sal/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sta")) {
                    require(['../views/sta/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sys")) {
                    require(['../views/sys/' + component + '.vue'], resolve);
                }
            }
        }
        fmRoutes.push(fmRouter);
    });
    return fmRoutes;
}

状态管理模式

  • state,驱动应用的数据源;
  • view ,以声明方式将 state 映射到视图;
  • actions ,响应在 view 上的用户输入导致的状态变化。

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

导航守卫

main.js

javascript 复制代码
router.beforeEach((to, from, next) => {
    /**
     * to:从哪来
     * from:去哪里
     * next:方法名,继续往下走
     * 菜单页面的初始化
     * **/
    if (to.path == '/') {
        next();
    } else {
        if (window.sessionStorage.getItem("user")) {
            //加载菜单项
            initMenu(router, store);
            next();
        } else {
            next('/?redirect=' + to.path);
        }
    }
})

在页面中引用组件

引用:

javascript 复制代码
import DepMana from '../../components/sys/basic/DepMana'
import PosMana from '../../components/sys/basic/PosMana'
import JobLevelMana from '../../components/sys/basic/JobLevelMana'
import EcMana from '../../components/sys/basic/EcMana'
import PermissMana from '../../components/sys/basic/PermissMana'

export default {
    name: "SysBasic",
    data(){
        return{
            activeName: 'depmana'
        }
    },
    //注册组件,key/value形式,若相同写一个即可。
    components:{
        DepMana,
        PosMana,
        JobLevelMana,
        EcMana,
        PermissMana
    }
}

使用:

vue 复制代码
<template>
    <div>
        <el-tabs v-model="activeName" type="card">
            <el-tab-pane label="部门管理" name="depmana"><DepMana></DepMana></el-tab-pane>
            <el-tab-pane label="职位管理" name="posmana"><PosMana></PosMana></el-tab-pane>
            <el-tab-pane label="职称管理" name="joblevelmana"><JobLevelMana></JobLevelMana></el-tab-pane>
            <el-tab-pane label="奖惩规则" name="ecmana"><EcMana></EcMana></el-tab-pane>
            <el-tab-pane label="权限组" name="permissmana"><PermissMana></PermissMana></el-tab-pane>
        </el-tabs>
    </div>
</template>
相关推荐
IT女孩儿1 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡2 小时前
commitlint校验git提交信息
前端
天天进步20152 小时前
Vue+Springboot用Websocket实现协同编辑
vue.js·spring boot·websocket
虾球xz2 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇2 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒2 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐3 小时前
前端图像处理(一)
前端
程序猿阿伟3 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒3 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript