Vue3+TS(一)——(主要是一些需要用到的webpack)

在这里插入图片描述

文章目录

认识Vue

是一套用于构建用户界面的渐进式框架
什么是渐进式框架?
表示我们可以在项目中亿点点来引入和适用Vue而不一定需要全部适用Vue来开发整个项目。

Vue3带来的变化

源码通过monorepo的形式来管理源代码

  • Mono:单个
  • Repo:repository仓库
  • 主要是将许多项目的代码存储在同一个repository中
  • 这样做的目的是多个包本身相互独立,可以有自己的功能逻辑、单元测试等,同时又在同一个仓库下方便管理
    -而且模块划分的更加清晰,可维护性、可拓展性更强。

源码使用TypeScript来进行重写

  • 在Vue2.x的时候,Vue使用Flow来进行类型检测
  • 在Vue3.x的时候,Vue的源码全部使用TypeScript来进行重构,并且vue本身对TypeScript支持也更好了。

使用Proxy进行数据劫持

  • 在Vue2.x的时候,是 使Object.defineProperty来劫持数据的getter和setter方法
  • 这种方式一致存在一个缺陷就是当给对象添加或者删除属性时,是无法劫持和监听的。
  • 所以在Vue2.x的时候不得不提供一些特殊的API,比如 s e t 或者 set或者 set或者delete,事实上都是一些hack方法,也增加了开发者学习新的API的成本
  • 而在Vue3.x开始,Vue使用Proxy来实现数据的劫持。
    删除了一些不必要的API:
  • 移除了实例上的 o n , on, on,off,和$once
  • 移除了一些特性:如filter、内敛模板等。

包括一些编译方面的优化:

  • 生成Block Tree 、 Slot编译优化、diff算法优化
    由Options API 到 Composition API:

  • 在Vue2.x的时候,我们会通过Options API来描述组件对象;

  • Options API包括data、props、methods、computed、生命周期等等这些选项;存在比较大的问题是多个逻辑可能是在不同的地方∶
    √ 比如created中会使用某一个method来修改data的数据,代码的内聚性非常差;

  • Composition API可以将相关联的代码放到同一处进行处理,而不需要在多个Options之间寻找;

Hooks函数增加代码的复用性∶

  • 在Vue2.x的时候,我们通常通过mixins在多个组件之间共享逻辑;
  • 但是有一个很大的缺陷就是mixins也是由一大堆的Options组成的,并且多个mixins会存在命名冲突的问题;
  • 在Vue3.x中,我们可以通过Hook函数,来将一部分独立的逻辑抽取出去,并且它们还可以做到是响应式的;

如何使用Vue

Vue的本质,就是一个JavaScript的库:

  • 刚开始我们不需要把它想象的非常复杂;
  • 我们就把它理解成一个已经帮助我们封装好的库;
  • 在项目中可以引入并且使用它即可。

那么安装和使用Vue这个JavaScript库有哪些方式呢?

  • 方式一︰在页面中通过CDN的方式来引入;

<script src="https: //unpkg.com/vue@next"></script>

-  什么是CDN呢?CDN称之为内容分发网络(Content Delivery Network或Content Distribution Network,缩写:CDN)
	- 它是指通过相互连接的网络系统,利用最靠近每个用户的服务器;
	- 更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户;

	- 来提供高性能、可扩展性及低成本的网络内容传递给用户;

- 常用的CDN服务器大致可以分为两种
	- 自己的CDN服务器︰需要购买自己的CDN服务器,目前阿里、腾讯、亚马逊、Google等都可以购买CDN服务器;
	- 口开源的CDN服务器∶国际上使用比较多的是unpkg、JSDelivr、cdnjs  ;
  • 方式二∶下载Vue的JavaScript文件,并且自己手动引入;

  • 方式三∶通过npm包管理工具安装使用它( webpack再讲);

  • 方式四︰直接通过Vue CLI创建项目,并目使用它

声明式编程和命令式编程

原生开发和Vue开发的模式和特点,我们会发现是完全不同的,这里其实涉及到两种不同的编程范式∶

  • 命令式编程和声明式编程﹔
  • 命令式编程关注的是“how to do”,声明式编程关注的是“what to do”,由框架(机器)完成“how”的过程;

在原生的实现过程中,我们是如何操作的呢?

  • 我们每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令;
  • 这样的编写代码的过程,我们称之为命令式编程;
  • 在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的;
    在Vue的实现过程中,我们是如何操作的呢?
  • 我们会在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods ;
  • 这样的编写代码的过程,我们称之为是声明式编程;
  • 目前Vue、React、Angular的编程模式,我们称之为声明式编程;

MVVM模型

MVC和MVVM都是一种软件的体系结构

  • MVC是Model -Vue -Controller的简称,是在前期被使用非常框架的架构模式,比如iOS、前端;
  • MVVM是Model-View-ViewModel的简称,是目前非常流行的架构模式;

通常情况下,我们也经常称Vue是一个MVVM的框架。
Vue官方其实有说明,Vue虽然并没有完全遵守MVVM的模型,但是整个设计是受到它的启发的。

在这里插入图片描述

template属性

  • 在使用createApp的时候,我们传入了一个对象,接下来我们详细解析一下之前传入的属性分别代表什么含义。
  • template属性:表示的是Vue需要帮助我们渲染的模板信息︰
    • 目前我们看到它里面有很多的HTML标签,这些标签会替换掉我们挂载到的元素(比如id为app的div )的innerHTML;
    • 模板中有一些奇怪的语法,比如{{}},比如@click,这些都是模板特有的语法

Vue提供了两种方式:

  • 方式一︰使用script标签,并且标记它的类型为x-template ;
  • 方式二∶使用任意标签(通常使用template标签,因为不会被浏览器渲染),设置id ;
    template元素是一种用于保存客户端内容的机制,该内容再加载页面时不会被呈现,但随后可以在运行时使用JavaScript实例化;
    在这里插入图片描述

data 属性

data属性是传入一个函数,并且该函数需要返回一个对象︰

  • 在Vue2.x的时候,也可以传入一个对象(虽然官方推荐是一个函数);
  • 在Vue3.x的时候,比如传入一个函数,否则就会直接在浏览器中报错;

data中返回的对象会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理

  • 所以我们在template中通过{{counter}}访问counter,可以从对象中获取到数据﹔
  • 所以我们修改counter的值时,template中的 {{counter}}也会发生改变;

methods属性

methods属性是一个对象,通常我们会在这个对象中定义很多的方法

  • 这些方法可以被绑定到template模板中;
  • 在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性;

在这里插入图片描述

methods方法绑定this

我们在methods中要使用data返回对象中的数据∶
那么这个this是必须有值的,并且应该可以通过this获取到data返回对象中的数据。
那么我们这个this能不能是window呢?
不可以是window,因为window中我们无法获取到data返回对象中的数据﹔但是如果我们使用箭头函数,那么这个this就会是window了.
为什么是window呢?
这里涉及到箭头函数使用this的查找规则,它会在自己的上层作用于中来查找this ;
最终刚好找到的是script作用于中的this,所以就是window ;

Vue基础——模板语法

React的开发模式:

  • React使用的jsx,所以对应的代码都是编写的类似于js的一种语法;
  • 之后通过Babel将jsx编译成 React.createElement函数调用;
    Vue也支持jsx的开发模式(后续有时间也会讲到)∶
  • 但是大多数情况下,使用基于HTML的模板语法;
  • 在模板中,允许开发者以声明式的方式将DOM底层组件实例的数据绑定在一起;
  • 在底层的实现中,Vue将模板编译成虚拟DOM渲染函数

所以,对于学习Vue来说,学习模板语法是非常重要的。

Mustche双大括号语法

如果我们希望把数据显示到模板(template )中,使用最多的语法是“Mustache”语法(双大括号)的文本插值。

  • 并且我们前端提到过,·data返回的对象·是有添加到·Vue的响应式系统·中;
  • 当·data中的数据发生改变时对应的内容也会发生更新
  • 当然,Mustache中不仅仅可以是data中的属性,也可以是一个JavaScript的表达式

各种指令

v-once指令

v-once用于指定元素或者组件只渲染一次∶
当数据发生变化时,素或者组件以及其所有的子元素将视为静态内容并且跳过;该指令可以用于性能优化 ;
在这里插入图片描述

在这里插入图片描述

v-text指令

用于更新元素的text-Content.相当于插值语法

v-html

默认情况下,如果我们展示的内容本身是html 的,那么vue并不会对其进行特殊的解析。如果我们希望这个内容被Vue可以解析出来,那么可以使用v-html来展示;

v-pre

v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签∶口跳过不需要编译的节点,加快编译的速度;

v-clock

■这个指令保持在元素上直到关联组件实例结束编译。
和CSS规则如[v-cloak] { display: none }一起用时,这个指令可以隐藏未编译的Mustache标签直到组件实例准备完毕。

v-bind的绑定属性

  • 前端讲的一系列指令,主要是将值插入到模板内容中。
  • 但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定
    • 比如动态绑定a元素的href属性;
    • 比如动态绑定img元素的src属性;
  • 绑定属性我们使用v-bind :
  • 缩写∶
  • 预期:any (with argument) | Object (without argument)
  • 参数:attrOrProp (optional)
  • 修饰符∶
    √ .camel -将 kebab-case attribute名转换为camelCase。
  • 用法︰动态地绑定一个或多个attribute,或一个组件prop 到表达式。
    v-bind有一个对应的语法糖,也就是简写方式。在开发中,我们通常会使用语法糖的形式,因为这样更加简洁。
    在这里插入图片描述
    绑定class介绍
    在开发中,有时候我们的元素class也是动态的,比如∶当数据为某个状态时,字体显示红色。当数据另一个状态时,字体显示黑色。
    绑定class有两种方式:
    对象语法
    数组语法
    绑定style介绍

我们可以利用v-bind:style来绑定一些CSS内联样式:
这次因为某些样式我们需要根据数据动态来决定;比如某段文字的颜色,大小等等;
css property名可以用驼峰式(camelCase)或短横线分隔(kebab-case,记得用引号括起来)来命名绑定class有两种方式:
对象语法
数组语法

动态绑定属性
在某些情况下,我们属性的名称可能也不是固定的:

  • 前端我们无论绑定src、href、class、style,属性名称都是固定的;
  • 如果属性名称不是固定的,我们可以使用:[属性名]=“值”的格式来定义;口这种绑定的方式,我们称之为动态绑定属性;

v-on绑定事件
前面我们绑定了元素的内容和属性,在前端开发中另外一个非常重要的特性就是交互
在前端开发中,我们需要经常和用户进行各种各样的交互:
这个时候,我们就必须监听用户发生的事件,比如点击、拖拽、键盘事件等等
在Vue中如何监听事件呢?使用v-on指令。
接下来我们来看一下v-on的用法∶
在这里插入图片描述

v-on参数传递

  • 当通过methods中定义方法,以供@click调用时,需要注意参数问题:
  • 情况一︰如果该方法不需要额外参数,那么方法后的()可以不添加。
    但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去
    情况二︰如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。

在这里插入图片描述
在这里插入图片描述

v-on修饰符

在这里插入图片描述

条件渲染

在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。
Vue提供了下面的指令来进行条件判断︰

  • v-if
  • v-else
  • v-else-if
  • v-show
    v-if、v-else、v-else-if用于根据条件来渲染某一块的内容:这些内容只有在条件为true时,才会被渲染出来;
    口这三个指令与JavaScript的条件语句if、else、else if类似;
    在这里插入图片描述
  • v-if的渲染原理:
  • v-if是惰性的;
  • 当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉;
  • 当条件为true时,才会真正渲染条件块中的内容;

template元素

因为v-if是一个指令,所以必须将其添加到一个元素上∶

  • 但是如果我们希望切换的是多个元素呢?
  • 此时我们渲染div,但是我们并不希望div这种元素被渲染;
  • 这个时候,我们可以选择使用template ;
    template元素可以当做不可见的包裹元素,并且在v-if上作用,但是最终template不会被渲染出来:
    有点类似于小程序中的block

template元素可以当做不可见的包裹元素,并且在v-if上作用,但是最终template不会被渲染出来:口有点类似于小程序中的block
v-show
v-show和v-if的用法看起来是一致的,也是根据一个条件决定是否显示元素或者组件︰
v-show和v-if的区别

  • 首先,在用法上的区别:
    v-show是不支持template ;
    v-show不可以和v-else一起使用;
  • 其次,本质的区别:
    • v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有渲染的,只是通过CSS的display属性来进行切换;
    • v-if当条件为false时,其对应的元素压根不会被渲染到DOM中;
  • 开发中如何进行选择呢?
    • 如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show ;
    • 如果不会频繁的发生切换,那么使用v-if ;

列表渲染

在真实开发中,我们往往会从服务器拿到一组数据,并且需要对其进行渲染。

  • 这个时候我们可以使用v-for来完成;
  • v-for类似于JavaScript的for循环,可以用于遍历一组数据;
    在真实开发中,我们往往会从服务器拿到一组数据,并且需要对其进行渲染。这个时候我们可以使用v-for来完成;
    v-for类似于JavaScript的for循环,可以用于遍历一组数据;

v-for基本使用

v-for的基本格式是"item in数组":

  • 数组通常是来自data或者prop,也可以是其他方式;
  • item是我们给每项元素起的一个别名,这个别名可以自定来定义;
    我们知道,在遍历一个数组的时候会经常需要拿到数组的索引: - 如果我们需要索引,可以使用格式:“(item, indek)in数组”;
  • 注意上面的顺序∶数组元素项item是在前面的,索引项index是在后面的;
    v-for也支持遍历对象,并且支持有一二三个参数:一个参数:
  • “value in object";
  • 二个参数: “(value, key) in object”;
  • 三个参数:“(value, key, index) in object”;v-for同时也支持数字的遍历︰
  • 每一个item都是一个数字;

数组更新检测

Vue将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括
push()
pop()
shift()
unshift()
splice()
sort()
reverse()

替换数组的方法

上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是会生成新的数组,比如filter()、concat(和slice).


v-for中的key是什么作用

  • 在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。这个key属性有什么作用呢?我们先来看一下官方的解释
    • key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes ;
    • 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
    • 使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;
  • 官方的解释对于初学者来说并不好理解,比如下面的问题:
    什么是新旧nodes,什么是VNode ?
    没有key的时候,如何尝试修改和复用的?

认识VNode

我们先来解释一下VNode的概念∶

  • 因为目前我们还没有比较完整的学习组件的概念,所以目前我们先理解HTML元素创建出来的VNode ;
  • VNode的全称是Virtual Node,也就是虚拟节点;
  • 事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode ;
  • VNode的本质是一个JavaScript的对象;
    在这里插入图片描述

虚拟DOM

如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree :
在这里插入图片描述

Vue3的Options-API

复杂data的处理方式

  • 我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
  • 但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示;
    • 比如我们需要对多个data数据进行运算三元运算符来决定结果数据进行某种转化后显示;
    • 在模板中使用表达式,可以非常方便的实现,但是设计它们的初衷是用于简单的运算;
    • 在模板中放入太多的逻辑会让模板过重和难以维护;
    • 并且如果多个地方都使用到,那么会有大量重复的代码;

我们有没有什么方法可以将逻辑抽离出去呢?

  • 可以,其中一种方式就是将逻辑抽取到一个method中,放到methods的options中;
  • 但是,这种做法有一个直观的弊端,就是所有的data使用过程都会变成了一个方法的调用;
  • 另外一种方式就是使用计算属性computed ;
    认识计算属性computed
    什么是计算属性呢?
  • 官方并没有给出直接的概念解释;
  • 而是说︰对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性;
  • 计算属性将被混入到组件实例中。所有getter和setter的 this 上下文自动地绑定为组件实例;

计算属性的用法︰

  • 选项: computed
  • 类型:{ [key: string]: Function \ { get: Function, set: Function } }

计算属性和methods的比较

  • 在上面的实现思路中,我们会发现计算属性和methods的实现看起来是差别是不大的,而且我们多次提到计算属性有缓存的
  • 接下来我们来看一下同一个计算多次使用,计算属性和methods的差异:
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

原因

  • 这是因为计算属性会基于它们的依赖关系进行缓存;
  • 数据不发生变化时,计算属性是不需要重新计算的;
  • 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算;

计算属性的setter和getter

  • 计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数。但是,如果我们确实想设置计算属性的值呢?
  • 这个时候我们也可以给计算属性设置一个setter的方法;
    在这里插入图片描述

事实上.Vue源码内部只是做了个逻辑

认识侦听器watch

什么是侦听器呢?

  • 开发中我们在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中;
  • 当数据变化时,template会自动进行更新来显示最新的数据;
  • 但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成了;

侦听器的用法如下︰

  • 选项: watch
  • 类型:{ [key: string]: string | Function | Object | Array}

侦听器watch的配置选项

  • 默认情况下,watch只是在侦听info的引用变化,对于内部属性的变化是不会做出响应的:

  • 这个时候我们可以使用一个选项deep进行更深层的侦听;

  • 注意前面我们说过watch里面侦听的属性对应的也可以是一个Object ;

  • 还有另外一个属性,是希望一开始的就会立即执行一次∶

    • 这个时候我们使用immediate选项;
    • 这个时候无论后面数据是否有变化,侦听的函数都会有限执行一次;
侦听watch的其他方式
  • 另外一个是Vue3文档中没有提到的,但是Vue2文档中有提到的是侦听对象的属性︰
    在这里插入图片描述

  • 还有另外一种方式就是使用$watch的API:

  • 我们可以在created的生命周期(后续会讲到)中,使用his.$watchs来侦听;

    • 第一个参数是要侦听的源;
    • 第二个参数是侦听的回调函数callback ;
    • 第三个参数是额外的其他选项,比如deep、immediate ;

知识的补充——浅拷贝的过程

在这里插入图片描述
对象是引用类型,直接copy的修改不会受到影响 但是对象的引用类型保存的是地址的时候,修改属性值是会受到影响的

方法二
引用lodash的库,const obj=_.clone();
在这里插入图片描述

对象的深拷贝(原生的方式)

JSON.stringify()转化为字符串,然后再用JSON.parse()的方法还原,还原的时候会在内存中生成一个新的对象,跟原来的对象没有任何关系,把完全生成 的新的对象赋值给一个变量。

方法二:lodash
_.cloneDeep()

v-model的基本使用

  • 表单提交是开发中非常常见的功能,也是和用户交互的重要手段:
    • 比如用户在登录、注册时需要提交账号密码;
    • 比如用户在检索、创建、更新信息时,需要提交一些数据;
  • 这些都要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用v-model指令来完成
    • :v-model指令可以在表单input、textarea以及select元素上创建双向数据绑定;
    • 它会根据控件类型自动选取正确的方法来更新元素;
    • 尽管有些神奇,但v-model本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理;
      在这里插入图片描述

官方有说到,v-model的原理其实是背后有两个操作:

  • v-bind绑定value属性的值;
  • v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

在这里插入图片描述
在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到data返回的对象中,再通过v-bind来进行值的绑定,这个过程就是值绑定。

v-model修饰符-lazy

lazy修饰符是什么作用呢?

  • 默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性 进行同步;
  • 如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为change事件,只有在提交时(比如回车)才会触发﹔

v-model修饰符-number

我们先来看一下v-model绑定后的值是什么类型的:

  • message总是string类型,即使在我们设置type为number也是string类型;
    在这里插入图片描述
  • 如果我们希望转化为数字类型,那么可以使用.number修饰符在这里插入图片描述
  • 另外,在我们进行逻辑判断时,如果是一个string类型,在可以转化的情况下会进行隐式转换的:
    • 下面的score在进行判断的过程中会进行隐式转化的;
      在这里插入图片描述
  • 如果要自动过滤用户输入的首尾空白字符,可以给v-model添加 trim 修饰符:
    在这里插入图片描述

Vue3组件化开发(一)

组件化思想:

  • 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展;
  • 但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了;
  • 如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目;

现在可以说整个的大前端开发都是组件化的天下,无论从三大框架(Vue、React、Angular),还是跨平台方案的Flutter,甚至是移动端都在转向组件化开发,包括小程序的开发也是采用组件化开发的思想。

我们需要通过组件化的思想来思考整个应用程序:

  • 我们将一个完整的页面分成很多个组件;
  • 每个组件都用于实现页面的一个功能块;
  • 而每一个组件又可以进行细分;
  • 而组件本身又可以在多个地方进行复用;

组件化是Vue、React、Angular的核心思想∶

  • 前面我们的createApp函数传入了一个对象App,这个对象其实本质上就是一个组件,也是我们应用程序的根组件;
  • 组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用;
  • 任何的应用都会被抽象成一颗组件树;
    在这里插入图片描述

注册组件的方式

  • 如果我们现在有一部分内容(模板、逻辑等),我们希望将这部分内容抽取到一个独立的组件中去维护,这个时候如何注册一个组件呢?

  • 我们先从简单的开始谈起,比如下面的模板希望抽离到一个单独的组件∶
    在这里插入图片描述

  • 注册组件分成两种∶

    • 全局组件:在任何其他的组件中都可以使用的组件;
    • 局部组件:只有在注册的组件中才能使用的组件;

我们先来学习一下全局组件的注册:

  • 全局组件需要使用我们全局创建的app来注册组件;
  • 通过component方法传入组件名称组件对象即可注册一个全局组件了;
  • 之后,我们可以在App组件的template中直接使用这个全局组件在这里插入图片描述
    在这里插入图片描述

当然,我们组件本身也可以有自己的代码逻辑:
比如自己的data、computed、methods等等
在这里插入图片描述
在通过app.component注册一个组件的时候,第一个参数是组件的名称,定义组件名的方式有两种

  • 方式一∶使用kebab-case (短横线分割符)
    • 当使用kebab-case(短横线分隔命名)定义一个组件时,你也必须在引用这个自定义元素时使用kebab-case例如 ;

在这里插入图片描述

  • 方式二∶使用PascalCase(驼峰标识符)
    • 当使用PascalCase (首字母大写命名)定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说和都是可接受的;

注册局部组件

  • 全局组件往往是在应用程序一开始就会全局组件完成,那么就意味着如果某些组件我们并没有用到,也会一起被注册
    • 比如我们注册了三个全局组件:ComponentA、ComponentB、ComponentC ;
    • 在开发中我们只使用了ComponentA、ComponentB,如果ComponentC没有用到但是我们依然在全局进行了注册,那么就意味着类似于webpack这种打包工具在打包我们的项目时,我们依然会对其进行打包
    • 这样最终打包出的JavaScript包就会有关于ComponerdC的内容,用户在下载对应的JavaScript时也会增加包的大小;

所以在开发中我们通常使用组件的时候采用的都是局部注册:

  • 局部注册是在我们需要使用到的组件中,通过components属性选项来进行注册;
  • 比如之前的App组件中,我们有data、computed、methods等选项了,事实上还可以有一个components选项;
  • 该components选项对应的是一个对象,对象中的键值对是组件的名称:组件对象

在这里插入图片描述

Vue的开发模式

  • 目前我们使用vue的过程都是在html文件中通过template编写自己的模板、脚本逻辑、样式等

  • 但是随着项目越来越复杂,我们会采用组件化的方式来进行开发∶

    • 这就意味着每个组件都会有自己的模板、脚本逻辑、样式等;
    • 当然我们依然可以把它们抽离到单独的js、css文件中,但是它们还是会分离开来;
    • 也包括我们的script是在一个全局的作用域下,很容易出现命名冲突的问题;
    • 并且我们的代码为了适配一些浏览器,必须使用ES5的语法;
    • 在我们编写代码完成之后,依然需要通过工具对代码进行构建、代码;
  • 所以在真实开发中,我们可以通过一个后缀名为.vuesingle-file components(单文件组件)来解决,并且可以使用webpack或者vite或者rollup等构建工具来对其进行处理。

单文件的特点
在这个组件中我们可以获得非常多的特性:

  • 代码的高亮;
  • ES6、CommonJS的模块化能力;
  • 组件作用域的CSS ;
  • 可以使用预处理器来构建更加丰富的组件,比如TypeScript、Babel、Less、Sass等;
    在这里插入图片描述

如何支持SFC

如果我们想要使用这一的SFC的.vue文件,比较常见的是两种方式:

  • 方式一∶使用Vue CLI来创建项目,项目会默认帮助我们配置好所有的配置选项,可以在其中直接使用.vue文件;
  • 方式二:自己使用webpack或rollup或vite这类打包工具,对其进行打包处理;

我们最终,无论是后期我们做项目,还是在公司进行开发~通常都会采用Vue CLI的方式来完成。

Webpack使用前提

  • webpack的官方文档是https://webpack.js.org/
    • webpack的中文官方文档https://webpack.docschina.org/
    • DOCUMENTATION:文档详情,也是我们最关注的
  • Webpack的运行是依赖Node环境的,所以我们电脑上必须有Node环境口
    • 所以我们需要先安装Node.js,并且同时会安装npm ;
    • 我当前电脑上的node版本是v14.15.5,npm版本是6.14.11(你也可以使用nvm或者n来管理Node版本) ;Node官方网站:https://nodejs.org/

在这里插入图片描述

Vue3组件化开发(二)(webpack部分各种插件和loader 有点复杂 回头详细看 )

认识webpack

  • 事实上随着前端的快速发展,目前前端的开发已经变的越来越复杂了∶
    • 比如开发过程中我们需要通过模块化的方式来开发;
    • 比如也会使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,通过sass、less等方式来编写css样式代码;
    • 比如开发过程中,我们还希望实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;
    • 比如开发完成后我们还需要将代码进行压缩、合并以及大他相关的优化;
      等等…
  • 但是对于很多的前端开发者来说,并不需要思考这些问题,日常的开发中根本就没有面临这些问题:
    • 这是因为目前前端开发我们通常都会直接使用三大框架来开发:Vue、React、Angular ;
    • 但是事实上,这三大框架的创建过程我们都是借助于脚手架(CL)的;
  • 事实上Vue-CLI、create-react-app、Angular-CLI都是基于webpack来帮助我们支持模块化、less.TypeScript、打包优化等的;

webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序;我们来对上面的解释进行拆解

  • 打包bundler : webpack可以将帮助我们进行打包,所以它是一个打包工具
  • 静态的static·:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器);
  • 模块化module : webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;
  • 现代的modern:我们前端说过,正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展;

Webpack安装

  • webpack的安装目前分为两个: webpack、webpack-cli
  • 那么它们是什么关系呢?
    • 执行webpack命令,会执行node_modules下的.bin目录下的webpack ;
    • webpack在执行时是依赖webpack-cli的,如果没有安装就会报错;
    • 而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程;
    • 所以在安装webpack时,我们需要同时安装webpack-cli(第三方的脚手架事实上是没有使用webpack-cli的,而是类似于自己的vue-service-cli的东西)
Microsoft Windows [版本 10.0.19045.3086]
(c) Microsoft Corporation。保留所有权利。

C:\Users\33195>npm install webpack

added 74 packages in 6s
npm notice
npm notice New minor version of npm available! 9.6.2 -> 9.7.2
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.7.2
npm notice Run npm install -g npm@9.7.2 to update!
npm notice

C:\Users\33195>npm install -g npm@9.7.2

removed 17 packages, and changed 64 packages in 9s

28 packages are looking for funding
  run `npm fund` for details

C:\Users\33195>npm --version
9.7.2

C:\Users\33195>npm install webpack-cli -g

added 117 packages in 7s

在这里插入图片描述
在这里插入图片描述
webpack的默认打包

  • 我们可以通过webpack进行打包,之后运行打包之后的代码

    • 在目录下直接执行webpack命令
  • 生成一个dist文件夹,里面存放一个main.js的文件,就是我们打包之后的文件:

    • 这个文件中的代码被压缩和丑化了;
    • 另外我们发现代码中依然存在ES6的语法,比如箭头函数、const等,这是因为默认情况下webpack并不清楚我们打包后的文件是否需要转成ES5之前的语法,后续我们需要通过babel来进行转换和设置;
  • 我们发现是可以正常进行打包的,但是有一个问题,webpack是如何确定我们的入口的呢?

    • 事实上,当我们运行webpack时,webpack会查找当前目录下的src/index.js作为入口;
    • 所以,如果当前项目中没有存在src/index.js文件,那么会报错;
  • 当然,我们也可以通过配置来指定入口和出口
    在这里插入图片描述

创建局部的webpack

  • 前面我们直接执行webpack命令使用的是全局的webpack,如果希望使用局部的可以按照下面的步骤来操作。
  • 第一步:创建package.json文件,用于管理项目的信息、库依赖等

npm init

  • 第二步:安装局部的webpack

npm install webpack-cli -D

  • 第三步:使用局部的webpack

npm webpack

  • 第四步:在package.json中创建script脚本,执行脚本打包即可
    在这里插入图片描述

npm run build

Vue项目加载的文件有哪些?

  • JavaScript的打包:
    • 将ES6转换成ES5的语法;
    • TypeScript的处理,将其转换成JavaScript ;
  • Css的处理:
    • CSS文件模块的加载、提取;
    • Less、Sass等预处理器的处理;
  • 资源文件img.font :
    • 图片img文件的加载;
    • 字体font文件的加载;
  • HTML资源的处理:
    • 打包HTML资源文件;
    • 处理vue项目的SFC文件.vue文件;

webpack5搭建Vue环境(这一部分有点复杂 回头二刷再发笔记。)

Vite2搭建Vue环境

Babel和devServer

为什么需要babel?

事实上,在开发中我们很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分:
开发中,我们想要使用ES6+的语法,想要使用TypeScript开发React项目,它们都是离不开Babel的;所以,学习Babel对于我们理解代码从编写到线上的转变过程至关重要;

那么Babel到底是什么呢?

  • Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript ;
  • 包括︰语法转换、源代码转换等;
    在这里插入图片描述
    Babel命令行使用
  • babel本身可以作为一个独立的工具(和postcss一样),不和webpack等构建工具配置来单独使用。
  • 如果我们希望在命令行尝试使用babel,需要安装如下库:
    @babel/core : babel的核心代码,必须安装;
    @babel/cli:可以让我们在命令行使用babel ;
    在这里插入图片描述
  • 使用babel来处理我们的源代码︰
    • src:是源文件的目录;
    • –out-dir :指定要输出的文件夹dist ;
      在这里插入图片描述


插件的使用
比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件。
在这里插入图片描述

  • 查看转换后的结果:我们会发现const 并没有转成var
    • 这是因为plugin-transform-arrow-functions,并没有提供这样的功能;
    • 我们需要使用plugin-transform-block-scoping 来完成这样的功能;
      在这里插入图片描述
      在这里插入图片描述
      Babel的预设preset
  • 但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设( preset ) :后面我们再具体来讲预设代表的含义;
  • 安装@babel/preset-env预设︰
    在这里插入图片描述
  • 执行如下命令
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Babel的底层原理

babel是如何做到将我们的**一段代码(ES6、TypeScript、React )**转成另外一段代码(ES5)的呢?

  • 从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?
  • 就是编译器,事实上我们可以将babel看成就是一个编译器。
  • Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码;
    Babel也拥有编译器的工作流程∶
  • 解析阶段( Parsing )
  • 转换阶段(Transformation )
  • 生成阶段( Code Generation )

Babel的执行阶段
在这里插入图片描述
当然这只是一个简化版的编译器工具流程,在每个阶段又会有自己具体的工作。
在这里插入图片描述
babel-loader

  • 在实际开发中,我们通常会在构建工具中通过配置babel来对其进行使用的,比如在webpack中。
  • 那么我们就需要去安装相关的依赖:
    如果之前已经安装了@babel/core,那么这里不需要再次安装;
    在这里插入图片描述

我们可以设置一个规则,在加载js文件时,使用我们的babel :
在这里插入图片描述

  • 我们必须指定插件才会生效
    在这里插入图片描述

  • 如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个preset , webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel。

  • 比如常见的预设有三个:

    • env
    • react
    • TypeScript
  • 安装preset-env :
    在这里插入图片描述
    Babel的配置文件
    像之前一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写︰

  • babel.config.json(或者js ,.cjs , .mjs )文件;

  • .babelrc.json(或者.babelrc , .js ,.cjs , .mjs )文件;

  • 它们两个有什么区别呢﹖目前很多的项目都采用了多包管理的方式 ( babel本身、element-plus、umi等);.

  • babelrc.json :早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的;

  • babel.config.json ( babel7 ):可以直接作用于Monorepos项目的子包,更加推荐;

Vue源码的打包

在这里插入图片描述
如上所示界面上是没有效果的:并且我们查看控制台会发现如下的警告信息:
vue打包不同版本解析:

  • vue(.runtime).global(.prod).js :

    • 通过浏览器中的

运行时+编译器vs仅运行时

在Vue的开发过程中我们有三种方式来编写DOM元素︰

  • 方式一: template模板的方式(之前经常使用的方式);
  • 方式二 :render函数的方式,使用h函数来编写渲染的内容;
  • 方式三:通过.vue文件中的template来编写模板﹔

它们的模板分别是如何处理的?

  • 方式二中的h函数可以直接返回一个虚拟节点,也就是vade节点;
  • 方式一和方式三的template都需要有特定的代码来对其进行解析︰
    - 方式三.vue文件中的template可以通过在vue-loader对其进行编译和处理;’
    - 方式一种的template我们必须要通过源码中一部分代码来进行编译;

所以,Vue在让我们选择版本的时候分为运行时+编译器vs仅运行时

  • 运行时+编译器包含了对template模板的编译代码,更加完整,但是也更大一些;
  • 仅运行时没有包含对template版本的编译代码,相对更小一些;

VSCode对SFC文件的支持

在前面我们提到过,真实开发中多数情况下我们都是使用SFC ( single-file components (单文件组件))
我们先说一下VSCode对SFC的支持︰

  • 插件一:Vetur,从Vue2开发就一直在使用的VSCode支持Vue的插件;
  • 插件二:Volar,官方推荐的插件(后续会基于Volar开友官方的VSCode插件);

devServer和VueCLI

为什么要搭建本地服务器?

  • 目前我们开发的代码,为了运行需要有两个操作∶
    • 操作一: npm run build,编译相关的代码;
    • 操作二∶通过live server或者直接通过浏览器,打开index.html代码,查看效果;
  • 这个过程经常操作会影响我们的开发效率,我们希望可以做到,当文件发生变化时,可以自动的完成编译和展示
  • 为了完成自动编译,webpack提供了几种可选的方式:
    • webpack watch mode ;
    • webpack-dev-server (常用);
    • webpack-dev-middleware ;

Webpack watch

  • webpack给我们提供了watch模式:

    • 在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译;
    • 我们不需要手动去运行npm run build指令了;
  • 如何开启watch呢?两种方式:

    • 方式一︰在导出的配置中,添加watch: true ;
    • 方式二∶在启动webpack的命令中,添加--watch的标识;
  • 这里我们选择方式二,在package.json的scripts 中添加一个watch 的脚本︰
    在这里插入图片描述

webpack-dev-server

上面的方式可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的∶

  • 当然,目前我们可以在VSCode中使用live-server来完成这样的功能;
  • 但是,我们希望在不使用live-server的情况下,可以具备live reloading(实时重新加载)的功能;

安装webpack-dev-server
在这里插入图片描述
修改配置文件,告知dev server,从什么位置查找文件:
在这里插入图片描述
webpack-dev-server在编译之后不会写入到任何输出文件,而是将bundle 文件保留在内存中:

  • 事实上webpack-dev-server使用了一个库叫memfs ( memory-fs webpack自己写的)

认识模块热替换(HMR)

什么是HMR呢?

  • HMR的全称是Hot Module Replacement,翻译为模块热替换;
  • 模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;

HMR通过如下几种方式,来提高开发的速度∶

  • 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
  • 只更新需要变化的内容,节省开发的时间;
  • 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式;
    如何使用HMR呢?
  • 默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可
  • 在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading ;

开启HMR

  • 修改webpack配置
    在这里插入图片描述
  • 浏览器可以看到如下效果:
    在这里插入图片描述
  • 但是你会发现,当我们修改了某一个模块的代码时,依然是刷新的整个页面
    • 这是因为我们需要去指定哪些模块发生更新时,进行HMR;
      在这里插入图片描述

框架的HMR

有一个问题∶在开发其他项目时,我们是否需要经常手动去写入module.hot.accpet相关的API呢?

  • 比如开发Vue、React项目,我们修改了组件,希望进行热更新,这个时候应该如何去操作呢?
  • 事实上社区已经针对这些有很成熟的解决方案了;
  • 比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;
  • 比如react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用react-refresh ) ;

HMR的原理

那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容呢?

  • webpack-dev-server会创建两个服务︰提供静态资源的服务( express)和Socket服务( net.Socket ) ;
  • express server负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析);
    HMR Socket Server,是一个socket的长连接
  • 长连接有一个最好的好处是建立连接后双方可以通信服务器可以直接发送文件到客户端);
  • 当服务器监听到对应的模块发生变化时,会生成两个文件.json ( manifest文件)和js文件( update chunk )
  • 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器);
  • 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新;
    在这里插入图片描述
    在这里插入图片描述

hotOnly、host配置

host设置主机地址:

  • 默认值是localhost ;
  • 如果希望其他地方也可以访问,可以设置为0.0.0.0;
    localhost 和0.0.0.0的区别:
  • localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1;
  • 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;

√正常的数据库包经常应用层–传输层–网络层–数据链路层–物理层;
√而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的;
√比如我们监听127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的;

  • 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序;

√比如我们监听0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的;

port、 open、compress

  • port设置监听的端口,默认情况下是8080
  • open是否打开浏览器∶
    • 默认值是false,设置为true会打开浏览器;
    • 也可以设置为类似于Google Chrome等值;
  • compress是否为静态文件开启gzip compression
    • 默认值是false,可以设置为true ;
      在这里插入图片描述

Proxy

proxy是我们开发中非常常用的一个配置选项,它的目的设置代理来解决跨域访问的问题:

比如我们的一个api请求是http://localhost:8888,但是本地启动服务器的域名是http://localhost:8000,这个时候发送网络请求就会出现跨域的问题;

那么我们可以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决我们的跨域问题了;
我们可以进行如下的设置

  • target:表示的是代理到的目标地址,比如/api-hy/moment会被代理到 http://localhost8888/api-hy/moment ;
  • pathRewrite :默认情况下,我们的/api-hy也会被写入到URL中,如果希望删除,可以使用pathRewrite ;
  • secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false ;
  • changeOrigin :它表示是否更新代理后请求的headers中host地址;

changeOrigin的解析
这个changeOrigin官方说的非常模糊,通过查看源码我发现其实是要修改代理请求中的headers中的host属性:

  • 因为我们真实的请求,其实是需要通过http://localhost:8888来请求的;
  • 但是因为使用了代码,默认情况下它的值时http://localhost:8000 ;
  • 如果我们需要修改,那么可以将changeOrigin设置为true即可;

historyApiFallback

  • historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误。
  • boolean值:默认是false
    • 如果设置为true,那么在刷新时,返回404错误时,会自动返回index.html的内容;
  • object类型的值,可以配置rewrites属性(了解)∶
    • 可以配置from来匹配路径,决定要跳转到哪一个页面;
  • 事实上devServer中实现historyApiFallback功能是通过connect-history-api-fallback库的:
    • 可以查看connect-history-api-fallback文档

resolve模块解析

resolve用于设置模块如何被解析∶

  • 在开发中我们会有各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库;
  • resolve可以帮助webpack从每个require/import语句中,找到需要引入到合适的模块代码;
  • webpack使用enhanced-resolve 来解析文件路径;

webpack能解析三种文件的路径

  • 绝对路径
    由于已经获得文件的绝对路径,因此不需要再做进一步解析。
  • 相对路径
    在这种情况下,使用import或require 的资源文件所处的目录,被认为是上下文目录;
    在import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径;
  • 模块路径
    在resolve.modules中指定的所有目录检索模块;
    √默认值是[‘node_modules’],所以默认会从node_modules中查找文件;
    我们可以通过设置别名的方式来替换初识模块路径,具体后面讲解alias的配置;

确定文件还是文件夹
如果是一个文件:

如果文件具有扩展名,则直接打包文件;
否则,将使用resolve.extensions选项作为文件扩展名解析;

如果是一个文件夹:

会在文件夹中根据resolve.mainFiles配置选项中指定的文件顺序查找;
resolve.mainFiles的默认值是[‘index’];
再根据resolve.extensions来解析扩展名;

extensions和alias配置

extensions是解析到文件时自动添加扩展名︰

  • 默认值是[‘.wasm’ , ‘.mjs’, ‘.js’, ‘.json’];
  • 所以如果我们代码中想要添加加载.vue或者jsx或者ts等文件时,我们必须自己写上扩展名;
    另一个非常好用的功能是配置别名alias :
  • 特别是当我们项目的目录结构比较深的时候,或者一个文件的路径可能需要…/…/…/这种路径片段;口我们可以给某些常见的路径起一个别名;
  • 我们可以给某些常见的路径起一个别名
    在这里插入图片描述

如何区分开发环境和生产环境

  • 目前我们所有的webpack配置信息都是放到一个配置文件中的: webpack.config.js
    • 当配置越来越多时,这个文件会变得越来越不容易维护;
    • 并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的;
    • 所以,我们最好对配置进行划分,方便我们维护和管理
  • 那么,在启动时如何可以区分不同的配置呢?
    • 方案一:编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可;
    • 方式二∶使用相同的一个入口配置文件,通过设置参数来区分它们;
      在这里插入图片描述
      入口文件解析
  • 我们之前编写入口文件的规则是这样的:./src/index,js,但是如果我们的配置文件所在的位置变成了config目录我们是否应该变成…/src/index.js呢?

如果我们这样编写,会发现是报错的,依然要写成./src/index.js ;
这是因为入口文件其实是和另一个属性时有关的context ;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值