Vue2学习文档3

配置代理

P96 方法一

正文

  • 跨域问题image-20220206161910281

原因

遇到蓝色框的这两东西一般都是跨域报错,原因是违背了同源策略-端口号不同,image-20220206162147985

请求实际上能正常发给服务器,但是当服务器发给浏览器响应请求时,浏览器发现服务器跨域,所以就没把数据给到我们。

几种解决方式

  1. 配置cors

只需要后端人员在返回响应时,加几个特殊的响应头即可,但是不好配置-配置后任何人都能找服务器要数据

  1. Jsonp

借助<script>标签的src属性在引入外部资源时,不受同源策略限制,但只能解决get请求,需要前后端配合

  1. 配置代理服务器
image-20220206163055983

如图所示通过一个代理服务器来解决跨域,为什么右边的线能走通呢?因为代理和后端都是服务区,两者通信不需要ajax(前端技术),用到时最原始http请求,同源策略不适用

如何配置一个代理服务器

1.使用nginx,但是比较成本高,需要懂后端

2.直接使用Vue-cli配置 官文

//vue.config.js
devServer:{ 
    proxy:'http://localhost:5000' //配置发给哪个服务器 
}
//缺点:1.根路径(public)下有配合内容不会走代理 2.不能配置多个代理

others

  • 浏览器输入地址敲回车-相当于发送了get()网络请求image-20220206161354552

  • 发送ajax请求-网络请求方式介绍

image-20220206161435548

如上图所示:

1.xhrfetch是都是js原生的,xhr用起来很麻烦,但是fetch符合Promise风格-更好用quan

2.JQueryaxios都是基于xhr封装的库,JQuery已过时,目前axios比较推荐,采用的也是Promise风格

  • 前端服务器根路径

public文件夹相当于本地服务器的根路径-8080里面有内容看public文件夹下有什么

image-20220206164535252image-20220206164423454这里浏览器输入地址get请求能拿到test.txt文件

axios请求会优先去找根路径下内容,如果有匹配不会发送给代理服务器了

image-20220206164858377

P97 方法二

code

proxy: {
  '/atguigu': { //使用前缀写法-可避免根路径冲突
    target: 'http://localhost:5000',// 代理目标的基础路径
    pathRewrite:{'^/atguigu':''}, //重写地址-删除前缀 p:如果后端接口已经有前缀了就不需要重写,这里的前缀只是加一个判断
    //P:重写路径-用空代替相似atguigu字符、因为后端接收的真正请求路径是不带前缀的	
    //vue中不写“ws”和“changeOrigin”,默认为true
  },
  '/demo': { //可以用多个前缀配置多个代理
    target: 'http://localhost:5001',
    pathRewrite:{'^/demo':''},
    // ws: true, //用于支持websocket
    // changeOrigin: true //用于控制请求头中的host值
  }
}

正文

  • changeOrigin

可以控制是否对服务器“说谎”,如右图所示值为true时发送配置(假的)的host值image-20220206173302937

  • 文档
  1. 方法一

vue.config.js配置规则:见上code

说明:

  1. 优点:配置简单,请求资源时直接发给**前端(8080)**即可。

  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。

  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发服务器优先匹配前端资源

  4. 方法二

vue.config.js配置规则:见上code

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
  2. 缺点:配置略微繁琐请求资源必须加前缀

github案例

P98 静态组件

other

  • 引入bootstrap报错问题
image-20220206181123069

import './assets/css/bootstrap.css' 这种方式引入检查比较严格,会发现没有bootstrap里面的字体image-20220206180836411

对于这种第三方css样式库,把文件放在public文件夹下,在main.js中用link引入更合适-检查不容易报错[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Tb7e3Ev-1652603446674)(Images/image-20220206181153948.png)]

<!--main.js-->
<!--引入第三方样式-->
<link rel='stylesheet' href="<%= BASE_URL%>css/bootstrap.css"> 

P99 列表展示

other

  • github提供官方接口

https://api.github.com/search/users?q=xxx

  • ``

这个符号专业词汇为模板字符串,里面可以用**${}**来放变量

  • github接口为https,而接口代理服务器为http,为什么没有报错跨域呢?

github的工程师用了cros来解决跨域,这个才是真正的解决跨域的方法

P100 完善案例

code

1.List.vue
info:{ //对于能组合的数据可以放在一个对象里,这样方便组件传参
	isFirst:true, 
	isLoading:false,
	errMsg:'',
	users:[]
}
this.$bus.$on('updateListData',(dataObj)=>{ 
	this.info = {...this.info,...dataObj} //通过解构覆盖的方式-避免属性减少
    //this.info = dataObj-这样有问题,如果dataObj只有三个属性,那info属性就不全了
})

2.Search.vue
//this.$bus.$emit('updateListData',isLoading:true,errMsg:'',users:[],isFirst:false)
//P:上面这种不推荐,因为还要保证每个参数顺序正确-麻烦
this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false})
//使用一个对象传参的方式可以不用考虑参数顺序
axios.get(`xxx`).then(
	response => {
		this.$bus.$emit('updateListData',			{isLoading:false,errMsg:'',users:response.data.items})
	},
	error => {
		this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message})
	}
)

P101 vue-resource

正文

  • 简介

vue-resource这个是vue插件****,然后也是基于xhr封装的,用法和axios**相同。

不过没怎么维护了,在vue1.0用的比较多,现在不推荐使用

  • 如何使用

在main.js中引入插件后,可以看到vm、vc对象上多了一个$http属性image-20220206210835974

other

  • 怎么使用vue插件-以vueResource为例
1.main.js
//引入插件
import vueResource from 'vue-resource'
//使用插件
Vue.use(vueResource)

2.Search.vue
this.$http.get(`xxx`).then( //用法和axios相同
    //P:$http和$bus差不多,猜测是不是所有vue的插件-都赋值在vc、vm中"$xxx"属性
	response => {
		console.log('请求成功了')
	},
	error => {
        console.log('请求失败了')
	}
)

插槽

P102 默认插槽

code

1.Category.vue
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
2.使用插槽.vue
<Category>
<ul>
	<li v-for="(g,index) in games" :key="index">{{g}}</li>  
</ul>
</Category>

如图

image-20210818180024965

P103 具名插槽

code

1.定义插槽.vue
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
2.使用插槽.vue
<ul slot="center">
	<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>

old

基本用法

image-20210818181019021

template可以用另一种写法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jgXk3GfS-1652603446676)(Images/image-20210818181127590.png)]

P104 作用域插槽(待复看)

如图:

image-20210818183506332

上文中的组件自身-子组件(Category);使用组件-父组件(App)

组件结构:

image-20210818183537000 image-20210818183625118

p:关键点template、scope、:data

Vuex

P105 简介

共享状态

P106 求和案例_纯vue版

见code……

P107 Vuex工作原理

image-20211210175341439

p:为什么要加actions,因为它能做特殊额外的请求处理

P108 搭建Vuex环境

讲解了vuex环境搭建,还有import js文件解析顺序

P:感觉挺重要的,有空补充下笔记

P109 求和案例_vuex版

见code……

P110 Vuex开发者工具的使用

见code……

P111 getters配置项

getters实际上就是store里的computed

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZgWldz1R-1652603446677)(Images/image-20211224155006676.png)]

P112 mapState与mapGetters-

P113 mapActions与mapMutations-

code

1.mapState
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
<!--<h1>当前求和为:{{$store.state.school}}</h1>  <!--直接使用vuex属性 -->
<h1>当前求和为:{{sum}}</h1> <!-- 使用计算属性 -->
computed:{ 
	/*sum(){ -自己写计算属性实现 
		return this.$store.state.sum
	}, */
    ...mapState({sum:'sum'}), //-mapState一般写法-帮我们生成计算属性
	...mapState(['sum']), //简写
}

2.mapGetters <!--用法同上-$store.getters --> 
...mapGetters({bigSum:'bigSum'})
...mapGetters(['bigSum'])
 

3.mapMutations
<!--写法差不多,注:模板要传参-$store.dispatch(x)--> 
<button @click="increment(n)">
...mapMutations({increment:'JIA'}),
...mapMutations(['JIA']),
 
4.mapActions
<!--写法同上-$store.commit(x)--> 
...mapActions({incrementOdd:'jiaOdd'})
...mapActions(['jiaOdd'])    
  • ...mapState为什么使用...

mapstate输出的实际上是一个对象image-20220306103228509,所以在computed对象中使用对象需要采用…来解构

正文

  • 调试工具输出-image-20220306103455203
  • mapActionsmapMutations-模板 绑定事件 传参
<button @click="increment(n)">+</button> 
//生成计算属性需要传value,所以要传参-不传的话increment默认会传event
...mapMutations({increment:'JIA')
/*incrementOdd(value){ 使用mapMutations生成的计算属性键值对实际上是这个
    this.$store.commit("JIA",value)
}*/
  • 文档

mapXxx方法:用于帮助我们映射Vuex-Xxx中的数据为计算属性

computed: {
    //靠mapXxx生成映射vuex的计算属性
    //mapState、mapGetters、mapMutations、mapActions、
	...mapXxx({sum:'sum',school:'school',subject:'subject'}), //对象写法
    ...mapXxx(['sum','school','subject']), //数组简写形式
},

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板绑定事件时传递好参数否则参数是事件对象

other

  • 关于对象的 属性简写注意点
mapState({sum:'sum'}),
//不可写成mapState({sum})-因为sum在这里输入的是"sum"字符串,简写会解析成sum变量

P114 多组件共享数据

other

  • 唯一id快速生成
import {nanoid} from 'nanoid'
const data={id:nanoid()}

P115 vuex模块化+namespace_1-

P116 vuex模块化+namespace_2-

3:07为啥使用/字符串- 6:30indexof使用 11:00控制台输出 getter写法和state手写不同 -code 14:53代码文件分离 -文档

code

1a.store-count.js
export default {
	namespaced:true,//命名空间-实现store模块化,注:必写-使用mapState简写
}
1b.store-person.js
const personAbout = {
  namespaced:true,
}
1b.store-index.js
import Vue from 'vue'
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'
Vue.use(Vuex)
const store = new Vuex.Store({
  modules: {
  		countAbout:countOptions,
		personAbout:personOptions
  }
})

2.Count.vue
//mapXxx针对模块化vuex的写法
computed:{
	...mapState('countAbout',['sum']), //第一个参数是指定命名Store
	...mapGetters('countAbout',['bigSum'])
},
methods: {
	...mapMutations('countAbout',{increment:'JIA'}),
	...mapActions('countAbout',{incrementOdd:'jiaOdd'})
},
    
3.Person.vue
//基础的store使用写法
computed:{
	sum(){
		return this.$store.state.countAbout.sum
	},
	firstPersonName(){
		return this.$store.getters['personAbout/firstPersonName']
        //与上面的map简写不同,因为mapXxx已自动处理好了 
        //为什么不能写成getters.personAbout.firstPersonName而要用"/"-因为你不是尤大,人家就喜欢这么设计api
	}
},
methods: {
	add(){
		this.$store.commit('personAbout/ADD_PERSON',personObj)
	},
	addPersonServer(){
		this.$store.dispatch('personAbout/addPersonServer')
	}
},

other

  • arr.indexOf(x)-返回在数组中可以找到一个给定元素第一个 索引,如果不存在,则返回**-1**。

路由

P117 路由简介

路由就是一组Key-value的对应关系

多个路由需要路由器就是vue-router

前端(url -component)后端(url-function)

P118 基本使用

image-20210813192018078

vue-router基本用法

P120 嵌套路由

code

1.router.js
{
	path:'/home',
	component:Home,
	children:[
		{
			path:'news', //嵌套路由
			component:News,
		},
	]
}

2.home.vue
<div>
	<router-link to="/home/news">News</router-link>
	<router-link to="/home/message">Message</router-link>
	<router-view></router-view> 
	<!-- 嵌套路由组件 --> 
</div>
  • 注:嵌套路由嵌套url的区别

old

嵌套路由也叫多级路由

使用语法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZCPduL5-1652603446678)(Images/image-20210816135238832.png)]

注:不要写"/"

P121 路由的query参数

code

<!-- 带query参数to的字符串写法 -->
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">xx</router-link>
<!-- 带query参数to的对象写法 -->
<router-link :to="{
	path:'/home/message/detail',
	query:{
		id:m.id,
		title:m.title
	}
}">
<!-- 上面两种router-link写法:推荐对象洗发-虽然代码长了但是可读性更好,具体选择还得看情况 -->

正文

  • 路由跳转引入的组件叫做组件路由

other

  • 开发经常会出现的报错 名词-模板编译失败image-20220306170935818

  • ``” 的学名-模板字符串

P122 命名路由

code

1.router.js
{
	path:'/home',
	children:[
		{
			path:'message',
			children:[
				{
					name:'simpleRoute',
					path:'detail',
				}
			]
		}
	]
}

2.Message.vue
//<router-link to="/home/message/detail">  //普通写法
<router-link :to="{name:'simpleRoute'}"> //简化写法-感觉也没简化多少hh,可能层级再多点更适用
<router-link :to="{ //简化写法-带query
	name:'simpleRoute',
	query:{
		id:m.id,
	}
}">

3.Detail.vue
<li>{{$route.query.id}}</li>

P123 路由的params参数

code

1.router.js
name:'simple',
path:'detail/:id/:title', //params可以写多个-:xx也叫占位符

2.Message.vue
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">xx</router-link>
<router-link :to="{ 
	name:'xiangqing', //注:对象写法的link必须要用name-path报错
	params:{
		id:m.id,
		title:m.title
	}
}"/>

3.Detail.vue
<li>{{$route.params.id}}</li>
<li>{{$route.params.title}}</li>
  • route对象params控制台输出-image-20220306185254071

P124 路由的props配置

code

{
	name:'xiangqing',
	path:'detail',
	component:Detail,
	props:{a:1,b:'hello'} //对象写法-以props形式传静态key-value给Detail组件
	props:true //布尔值写法-所有params
	props($route){ //函数写法-传动态对象,参数为$route
		return {
			id:$route.query.id,//内容自定义
			title:$route.params.title,
			b:'hello' 
		}
	}
	props({query:{id}}){ //解构赋值连续写法-更简洁但语义化不行并不推荐
		return {
			id,
		}
	}
}

p125 router-link的replace属性

code

<router-link replace to="/home/news">News</router-link> <!-- 开启replace模式-默认为push --> 

正文

  • pushraplace模式区别

浏览器左上角箭头image-20220306200200800可操作路由历史,路由历史的存储是-先进先出

1.pushimage-20220306200136538 2.replaceimage-20220306200440922

P126 编程式路由导航

code

//$router的两个API
this.$router.push({
	//name:'xiangqing',
    path:'/xxx',
		params:{
			id:xxx,
			title:xxx
		}
})

this.$router.replace({
	//name:'xiangqing',
    path:'/xxx',
		params:{
			id:xxx,
			title:xxx
		}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退

正文

  • 什么叫编程导航-不借助router-link的导航
  • 文档

作用:不借助 实现路由跳转,让路由跳转更加灵活

other

  • router-link不足-只能及时触发不能延时

P127 缓存路由组件

code

1.Home.vue
<!-- <keep-alive :include="['News','Message']"> --> <!-- 缓存多个 -->
<keep-alive include="News"> //区分标识符为组件名
	<router-view></router-view>
</keep-alive>

2.News.vue
name:'News', 

正文

  • 跳转路由默认前一个页面组件销毁-销毁后返回组件会重新挂载,可以用beforedestroy判断
  • 文档

作用:让不展示的路由组件保持挂载,不被销毁。

补充

<!-- 需要缓存的视图组件 -->

<router-view v-if="$route.meta.keepAlive">
</router-view>
</keep-alive>

<!-- 不需要缓存的视图组件 -->

<router-view v-if="!$route.meta.keepAlive">
</router-view>

P128 两个新的声明周期钩子

正文

  • 使用缓存路由组件可能会出现的问题

就是缓存路由组件不能触发beforedestroy,那就无法进行像定时器销毁这样的操作,所以vue有提供专属路由生命周期替代

//News.vue 缓存路由组件
activated() {
	console.log('News组件被激活了')
	this.timer = setInterval(() => {},16)
},
deactivated() {
	console.log('News组件失活了')
	clearInterval(this.timer)
},
  • vue2生命周期图没有出现的三个钩子-activated、deactivated、nextTick

  • 文档

作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

具体名字:

  1. activated路由组件被激活时触发。
  2. deactivated路由组件失活时触发。

P129 全局前置_路由守卫-

P130 全局前置_路由守卫-

P131 独享路由守卫-

code

//router.js
routes:[
  {
	name:'xiaoxi',
	path:'message',
	component:Message,
    //title:'消息'-router只展示含有的配置项
	meta:{isAuth:true,title:'消息'}, //meta-路由元信息,可以放一些自定义参数
    beforeEnter: (to, from, next) => {……} //独享路由守卫-只有前置    
  }
]

//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
	if(to.meta.isAuth){ //判断是否需要鉴权
		if(localStorage.getItem('school')==='atguigu'){
			next() //只有调用next时路由才跳转
		}else{
			alert('学校名不对,无权限查看!')
		}
	}else{
		next()
	}
})

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{ //后置没有next-常用来改变网页名称
	if(to.meta.title){ 
		document.title = to.meta.title //修改网页的title-像这种修改还是放后置合适,前置要判断太多
	}else{
		document.title = 'vue_test'
	}
})

P132 组件内路由守卫

code

//About.vue-路由组件
//通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
	if(to.meta.isAuth){ //判断是否需要鉴权
		if(localStorage.getItem('school')==='atguigu'){
			next() //注:加next放行
		}else{
			alert('学校名不对,无权限查看!')
		}
	}else{
		next()
	}
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
	next() //注:加next放行
}
  • 组件内守卫和路由守卫的区别

组件内守卫的enter和leave是进入离开组件的意思,不要和路由守卫的前置后置弄混,感觉只要根据方法名语义理解即可-且用的不多

P133 history和hash模式

1:00不随http发服务器 2:00#后算hash值 4:23模式配置-code 6:00兼容性8:40serve的本质

10:50打包文件部署 12:00~简单服务器搭建express 14:40前端文件存放在后端的文件名 16:30前端打包文件在后端项目展示 17:30刷新页面请求报错 20:59#后内容不传后端请求资源 22:38使用history不报错解决 和后端合作 23:30后端解决路由冲突 -文档

code

const express =require('express')
const history =require('connect-history-api-fallback')
const app =express()
app.use(history()) 
app.ues(express.static(__direname+'/static')) //本地资源
app.get('/person',(req,res)=>{
	res.send({
        name:'tom',
        age:18,
    })
})
app.listen(5005,err=>{
	if(!err) console.log('服务器启动成功')
})
  • 后端项目文件结构-image-20220313202947056
  • app.ues(express.static(__direname+'/static'))-可以让浏览器通过发送请求拿到前端文件image-20220313203117610
  • 使用histroy后端处理刷新页面报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HgQpGD8S-1652603446680)(Images/image-20220313203451867.png)]

点击前端tab切换路由不走网络请求,但是刷新页面会走网络请求,如果后端没有处理还是history模式,后端就会报错(没有对应路由),但是使用hash就可以解决,因为hash值不会传后端

  • 前端使用history路由模式后端配合解决办法-使用connect-history-api-fallback插件

正文

  • hash值不随http发送后端服务器
  • 如何配置路由模式

在vueRouter()构造函数中写配置mode:'history',默认为hash

  • yarn serve的本质-开启指定端口号内置服务器image-20220313201934821

  • 打包文件必须在服务器部署才能正常打开-image-20220313202209855image-20220313202123971

  • 前端文件存放在后端文件名-static或者publicimage-20220313202617729

  • 文档

对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

hash值不会包含HTTP 请求中,即:hash值不会带给服务器

hash模式:

  1. 地址中永远带着#号,不美观
  2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
  3. 兼容性较好。

history模式:

  1. 地址干净,美观
  2. 兼容性和hash模式相比略差
  3. 应用部署上线时需要后端人员支持,解决刷新页面 服务端404的问题。

Element UI

P134 element-ui基本使用

……

P135 element-ui按需引入

code

1.babel.config.js
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
		["@babel/preset-env", { "modules": false }],
  ],
	plugins:[
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

2.main.js
//按需引入
import { Button} from 'element-ui';
//引入全局组件
Vue.component(Button.name, Button);//Button.name-即是Button别名el-button

3.App.vue
<el-button>默认按钮</el-button>
  • element-ui官文的坑

由于官文没有及时 根据vuecli 更新,所以会有很多坑

  1. .babelrc文件改名为babel.config.js
  2. image-20220313211433503-配置改成"@babel/preset-env"才能解决

正文

  • npm -D的意思-就是开发依赖,禹神说:按需明显是开发需要的,如果生产环境早就打包完了?没懂,不过他说这部分可以看webpack教程

后端人员支持,解决刷新页面 服务端404的问题。

Element UI

P134 element-ui基本使用

……

P135 element-ui按需引入

code

1.babel.config.js
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset',
		["@babel/preset-env", { "modules": false }],
  ],
	plugins:[
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

2.main.js
//按需引入
import { Button} from 'element-ui';
//引入全局组件
Vue.component(Button.name, Button);//Button.name-即是Button别名el-button

3.App.vue
<el-button>默认按钮</el-button>
  • element-ui官文的坑

由于官文没有及时 根据vuecli 更新,所以会有很多坑

  1. .babelrc文件改名为babel.config.js
  2. image-20220313211433503-配置改成"@babel/preset-env"才能解决

正文

  • npm -D的意思-就是开发依赖,禹神说:按需明显是开发需要的,如果生产环境早就打包完了?没懂,不过他说这部分可以看webpack教程

xx

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值