CSS
Flex弹性布局
容器的属性:
flex-direction: 决定主轴的方向 row行左从左到右、row-reverse行右从右到左、column列从上到下、column-reverse行从下到上
flex-wrap: 如何换行 normal单行排列、wrap多行自上而下、wrap-reverse多行自下而上
flex-flow: 是flex-direction和flex-wrap的简写形式
justify-content: 定义了项目在主轴上的对齐方式 flex-start从起点线开始排序、flex-end从终点线开始排序、center居中布局、space-between项目均匀分布第一项在起点线最后一项在终点线、space-around项目均匀分布每个项目两侧有相同的留白,项目之间的距离为留白之和、space-evenly项目均匀分布项目距离和项目与边框距离相等
align-items: 定义项目在交叉轴上如何对齐 flex-start交叉轴起点对齐、flex-end交叉轴终点对齐、center交叉轴中间对齐、baseline交叉轴第一行文字基线对齐
align-content: 定义了多根轴线的对齐方向及额外空间分配(类似justify-content) flex-start从起点线开始排序、flex-end从终点线开始排序、center居中排序、space-between项目均匀分布第一项在起点线最后一项在终点线、space-around项目均匀分布、每个项目上下有相同留白项目距离为留白之和
项目属性:
order: 定义项目的排列顺序。数值越小,排列越靠前 默认值0 可以为负值
flex-grow: 定义项目的放大比例 默认值0 负值无效
flex-shrink: 定义项目的缩小比例 默认值 0表示不缩小 负值无效
flex-basis: 定义了在分配多余空间之前,项目占据的主轴空间
flex: flex-grow, flex-shrink和flex-basis的简写
align-self: 允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性
介绍css3中position:sticky
粘性定位可以被认为是相对定位和固定定位的混合。
该元素定位不对后续元素造成影响,当元素被粘性定位时,后续元素的位置仍按照该元素未定位时的位置来确定
CSS3新特性有哪些
边框:border-radius、box-shadow、border-image
背景:background-image、background-size、background-origin、background-clip
渐变:linear-gradient(线性渐变)、radial-gradient(径向渐变)
文本效果:text-shadow、text-overflow(如何显示溢出内容)、word-wrap(换行)、word-break(指定换行规则)
字体:@font-face
2D转换(transform):translate(位移)、rotate(旋转)、scale(缩放)、skew(翻转)、matrix(将所有2d转换方法组合在一起)
3D转换:translate、rotate、scale多一个Z轴或者3d
过渡(transition):元素从一种样式逐渐变成另一种的效果。transition是简写的四种属性,属性名称(transition-property) 过渡花费时间(transition-duration) 过渡效果(transition-timing-function) 过渡延迟执行时间(transition-delay)
transition-timing-function: linear(匀速) ease(慢-快-慢) ease-in(慢-快) ease-out(快-慢) ease-in-out(慢-快-慢)
动画:@keyframes animation(简写属性、用于设置六个动画属性)
animation: animation-name(动画名称)、animation-duration(动画时间)、animation-timing-function(动画的速度曲线)、animation-delay(动画延迟执行时间)、animation-iteration-count(动画播放的次数)、animation-direction(是否轮流反向播放动画)
JS
每个实例对象都有一个私有属性(proto)指向它构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(proto),层层往上直到一个对象的原型对象为Null。
遵循ECMAScript标准,someObject.[[Prototype]]符号用于执行someObject的原型。从ES6开始,[[Prototype]]可以通过Object.getPrototypeOf()和Object.setPrototypeOf()访问器访问。这个等同于JavaScript的非标准但许多浏览器实现的属性proto
但它不应该与构造函数func的prototype属性相混淆。被构造函数创建的实例对象的[[prototype]]指向func的prototype属性。Object.prototype属性表示Object的原型对象
原型链的定义:每个对象拥有一个原型对象,对象以其原型为模板,从原型继承属性和方法。原型对象也拥有原型,并从中继承方法和属性,一层一层,以此类推,这种关系常被称为原型链。
proto和prototype:对象的原型proto与构造函数的prototype属性之间的区别,前者是每个实例上都有的属性,后者是构造函数的属性。也就是说,Object.getPrototypeOf(new Foobar())和Foobar.prototype指向着同一个对象。
prototype:每个函数都有一个特殊的属性叫做prototype
什么是原型和原型链?
每个对象都有一个私有属性(proto)指向它构造函数的原型对象(prototype)。
对象从原型继承属性和方法。原型对象也有自己的原型对象,层层往上直到一个对象的原型对象为null,这种关系被称为原型链。注:proto是javascript非标准但浏览器实现的属性
继承方法有哪些?
1.对象冒充
继承ClassA的方法,最后删除对ClassA的引用
function ClassA () {}
function ClassB () {
this.newMethod = ClassA;
this.newMethod(args);
delete this.newMethod;
}
2.call()方法
使用call()方法指定this的指向,实现继承。
function ClassB (color, name) {
ClassA.call(this, color);
this.name = args;
this.sayName = function () {
return this.name;
}
}
3.apply()方法
与call()方法继承一样。区别是call和apply的区别,参数传递不一样
4.原型链
function ClassA() {}
ClassA.prototype.color = 'blue';
ClassA.prototype.getColor = function () {
return this.color;
}
function ClassB () {};
ClassB.prototype = new ClassA();
使用原型链继承,将需要父类实例赋值给子类原型对象,可继承所有的属性和方法。
缺点是:原型链上的属性和方法修改,子类也会被影响,没有传递参数。
5.组合继承
使用构造函数定义属性,用原型定义方法;用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法
function ClassA (color) {
this.color = color;
}
ClassA.prototype.getColor = function () {
return this.color;
}
function ClassB (color, name) {
ClassA.call(this, color);
this.name = name;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function () {
return this.name;
}
缺点:父类的构造函数被调用两次。
a.设置子类原型对象,调用一次;ClassB.prototype = new ClassA();
b.实例化对象时调用一次;ClassA.call(this, color);
6.寄生式继承
在不需要实例化父类构造函数时,也能继承父类原型对象上的方法;
function InheritPrototype (child, father) {
var prototype = Object.create(father.prototype); // 浅拷贝父类的原型对象
prototype.constructor = child; // 指向原型对象的构造器为子类
child.prototype = prototype; // 将原型对象赋值给子类的原型对象
}
new的作用?
1.创建一个空的对象;
2.设置该对象的构造函数到另一个对象;
3.将新创建的对象作为this的上下文;
4.如果该函数没有返回对象,则返回this;
事件循环机制是什么?
1.运行时概念:
函数调用形成一个栈帧,放到栈中,根据后进先出原则直到栈清空;
对象被分配到一个堆中,即用以标识一大块非结构化的内存区域;
一个javascript运行时包含了一个待处理的消息队列。每一个消息都关联这一个用以处理这个消息的函数。
2.JAVASCRIPT事件循环的运行机制:
1.所有同步任务都在主线程上执行,形成一个执行栈;
2.主线程之外,还存在一个任务队列。只要异步任务有了运行结果,就在任务队列之中放置一个事件;
3.一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,那些对应的异步任务于是结束等待状态,进入执行站开始执行;
4.主线程不断重复上面的第三步;
JS只有一个主线程,主线程执行完执行栈的任务后去检查异步的任务队列,如果异步事件触发,则将其加到主线程的执行栈,主线程不断重复这个过程;
vue和react的哪里不同?
1.React严格上只针对MVC的view层,Vue则是MVVM模式
2.virtual DOM不一样。vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树
而对React而言,每当应用的状态被改变时,全部组件都会重新渲染,如要避免不必要的子组件重新渲染需要用shouldComponentUpdate.
3.组件写法不一样。React推荐使用JSX。Vue推荐用webpack + vue-loader的单文件组件格式,即html,css,js写在同一个文件。
4.数据绑定:vue实现了数据的双向绑定,react数据流动是单向的
5.state对象在react应用中不可变,需要使用setState方法更新状态;
vue在state对象不是必须的,数据由data属性在vue对象中管理
常见Http请求头
- Accept 可接受的响应内容类型 Accept: text/plain
- Accept-Charset 可接受的字符集 Accept-Charset: utf-8
- Accept-Encoding 可接受的响应内容的编码方式 Accept-Encoding: gzip, deflate
- Accept-Language 可接受的响应内容语言列表 Accept-Language: en-US
- Accept-Datetime 可接受的按照时间来表示的响应内容版本 Accept-Datetime: xxx
- Authorization 用于表示HTTP协议中需要认证资源的认证信息 Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE==
- Cache-Control 用来指定当前的请求/回复中的,是否使用缓存机制 Cache-Control: no-cache
- Connection 客户端想要优先使用的连接类型 Connection: keep-alive
- Cookie 由之前服务器通过Set-Cookie设置的一个HTTP协议Cookie
- Content-Length 以8进制表示的请求体的长度 Content-Length: 348
- Content-MD5 请求体的内容二进制MD5散列值,以Base64编码的结果
- Content-Type 请求体的MIME类型 Content-Type: application/x-www-form-urlencoded
- Date 发送该消息的日期和时间
- Expect 表示客户端要求服务器做出特定的行为
- From 发起次请求的用户的邮件地址
- Host 表示服务器的域名以及服务器所监听的端口号
- Origin 发起一个针对跨域资源共享的请求
- User-Agent 浏览器的身份标识字符串
防抖和节流?
防抖:函数在频繁需要触发情况时,只有等足够空闲的时间才去执行一次。
节流:预定一个函数只有在大于等于执行周期的时候才去执行
介绍观察者模式
意图:定义对象间一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都会得到通知并被自动更新
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作
介绍中介者模式
意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
观察者和订阅-发布的区别,各自用在哪里
观察者由具体目标调用,订阅-发布是统一由调度中心调用。
观察者模式的订阅者与发布者存在依赖关系,而订阅-发布模式不会。
介绍http2.0新特性
- 在应用层(HTTP)和传输层(TCP)之间增加一个二进制分帧层
二进制分帧:在二进制分帧层上,HTTP2.0会将所有传输信息分割为更小的消息和帧,并对它们采用二进制格式的编码将其封装 - 首部压缩:所有首部键必须全部小写
- 流量控制
- 多路复用
- 请求优先级
- 服务器推送
通过什么做到并发请求
Promise处理并发请求
浏览器事件流向
DOM事件传播包括三个阶段:
- 捕获
- 事件目标处理函数
- 冒泡
介绍事件代理以及优缺点
事件委托原理:事件冒泡机制
优点:
- 大量节省内存占用,减少事件注册
- 可以实现当新增子对象时,无需再对其进行事件绑定
缺点:
上诉1中的需求才会使用,使用场景少。如果把所有事件都用事件委托,可能会出现事件误判
tcp3次握手
第一次握手:建立连接时,客户端发送syn包到服务器,并进去SYN_SENT状态,等待服务器确认;(SYN: 同步序列编号)
第二次握手:服务器收到syn包,必须确认客户端的syn,同时自己也发送一个syn包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入established状态,完成三次握手
bind、call、apply的区别
相似之处:
- 都是用来改变函数的this指向
- 第一个参数都是this指向的对象
- 都可以利用后续参数传参
区别:
call和apply都是对函数的直接调用
call后续参数是单独一个一个传
apply第二个参数是一个数组
bind方法返回的仍然是一个函数
闭包的定义和作用
闭包是有权访问另一个函数作用域中变量的函数。
特性:
- 函数外部的代码无法访问函数体内部的变量,而函数体内部的代码可以访问函数外部的变量
- 即使函数已经执行完毕,在执行期间创建的变量也不会销毁,因此每运行一次函数就会在内存中留下一组变量。
介绍下跨域
跨域是指协议、域名、端口不一致,出于安全考虑,跨域的资源之间无法交互。
跨域解决方案
- 通过jsonp跨域
- document.domain + iframe跨域
- location.hash + iframe
- window.name + iframe 跨域
- postMessage跨域
- 跨域资源共享(CORS)
- nginx代理跨域
- nodeJs中间件代理跨域
- WebSocket协议跨域: 是HTML5一种新协议,实现了浏览器和服务器全双工通信,同时允许跨域通信
ES6
ES6的新特性有哪些?
- let和const
- 字符串模板
- 变量的解耦赋值
- 箭头函数
- Array.from Array.of
- Set数据结构(类似数组,但是成员的值都是唯一的,没有重复的值)
- Proxy 作用是对目标对象提供拦截行为
- Promise对象
- async 函数
- module语法
箭头函数的注意点:
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当做构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。
- 不可以使用yield命令,因为箭头函数不能作为Generator函数
ES6模块和CommonJS模块的差异:
- CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用
CommonJS模块输出一个值,模块内部的变化不会影响该值,除非写一个函数才能获取内部改动后的值。
ES6输出的值,如果原始值改变,那输出的值也会跟着变。 - CommonJS模块是运行时加载,ES6模块是编译时输出接口
CommonJS记载的是一个对象,该对象只有在脚本运行完才会生成。
ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
promise、async有什么区别?
- Async是基于Promise实现的,它不能用于普通的回调函数
- Async使得异步代码看起来像同步代码
- Async函数会返回一个Promise对象
- Async让 try/catch可以同时处理同步和异步错误
- Async函数让代码更简洁,更符合语义
Webpack
Webpack3和Webpack4有什么区别?
1.webpack4新增mode选项。根据配置类型将其设置为生产或开发。
2.弃用部分插件,包括压缩js的UglifyJsPlugin,DefinePlugin创建一个在编译是可以配置的全局常量的插件;
压缩插件改用Optimization选项,DefinePlugin内置到new Webpack里面
webpack4常用Loading有哪些?
babel-loader:允许使用babel和webpack转译js文件
css-loader
postcss-loader
sass-loader
style-loader
less-loader
url-loader
file-loader
webpack4常用的plugin有哪些?
webpack.Defineplugin:允许创建一个在编译时可以配置的全局常量
webpack.HotModuleReplacementPlugin:热替换模块
webpack.optimize.LimitChunkCountPlugin:可以通过合并的方式,处理你的chunk,以减少请求数
UglifyjsWebpackPlugin:压缩JS代码
htmlWebpackPlugin
CleanWebpackPlugin
webpack配置优化
优化Loader配置: 1. 优化正则匹配 2.通过cacheDirectory开启缓存 3. 通过include、exclude来减少被处理的文件
优化resolve.modules配置
优化resolve.alias配置,通过别名来将原导入路径映射成一个新的导入路径
优化resolve.extensions配置,常用的后缀放在前面,而且后缀列表要尽可能的小
减少冗余代码 babel-plugin-transform-runtime
使用HappyPack多进程解析和处理文件
开启模块热替换
提取公共代码 chunk(相同资源被重复加载,首屏加载缓慢的问题)
按需加载代码
优化SourceMap
mode模式
React
React组件生命周期的阶段是什么?
React组件的生命周期有三个不同的阶段:
- 初始渲染阶段:这是组件即将开始其生命之旅并进入DOM的阶段
- 更新阶段:一旦组件被添加到DOM,它只有在prop或状态发生改变时才能更新或重新渲染。
- 卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从DOM中删除。
React组件的生命周期方法
- componentWillMount() 在渲染之前执行,在客户端和服务端都会执行
- componentDidMount() 仅在第一次渲染后在客户端执行
- componentWillReceiveProps() 当从父类接收到props并且在调用另一个渲染器之前调用
- shouldComponentUpdate() 根据特定条件返回true或false。如果你希望更新组件,请返回true否则返回false。默认情况下,它返回false
- componentWillUpdate() 在DOM中进行渲染之前调用
- componentDidUpdate() 在渲染发生后立即调用
- componentWillUnmount() 从DOM卸载组件后调用,用于清理内存空间
React组件通信
父传子:使用props对象,包含需要传递给子组件的参数,props是不可修改的。传递方法:定义方法名传递一个函数进去
子传父:由于子组件不能修改props,需要修改props的值时,需要使用父组件传递的函数,调用传递的函数实际是父组件调用自己的函数,并修改自身的值,达到修改props的效果
兄弟:利用redux实现
React无状态函数
无状态函数是使用函数构建的无状态组件,无状态组件传入props和context两个参数,它没有state,除了render(),没有其他生命周期方法。
Context描述:
无需为每层组件手动添加props,就能在组件树间进行数据传递的方法。
创建一个Context对象
React.createContext(defaultValue);
说一说Forwarding Refs有什么用?
是父组件用来获取子组件的dom元素
const refs = React.createRef();
将refs传递给子组件,使用refs.current可以获取子组件的dom元素
render() {
}
refs.current获取的就是Child这个组件,是一个对象{context: {}, props: {}, refs: {}, state: null, updater: {} ...}
如果使用React.forwardRef((props, ref) => (
))
refs.current获取的就是子组件里的Input的DOM元素
简述一下virtual DOM如何工作?
- 当数据发生变化时,会引起组件重新渲染,整个UI都会以virtual DOM的形式重新渲染
- 然后收集差异也就是diff新的virtual dom和老的virtual dom的差异
- 最后把差异队列里的差异,比如增加节点、删除节点、移动节点更新到真实的DOM上
什么是受控组件和非受控组件?
受控组件是表单元素的值是通过React组件的state来控制的,并且使用onChange方法通过setState()更新state属性
非受控组件是表单数据将交由DOM节点来处理,使用ref从DOM节点中获取表单数据
什么是状态提升?
有时候几个组件需要共用状态数据的情况下,我们将共享的状态提升至他们最近的父组件当中进行处理。
Portals是什么?
portals提供了一种将子节点渲染到存在于父组件之外的DOM节点的方法。
ReactDOM.createPortal(child, container);
第一个参数child的任何可渲染的React子元素,例如一个元素、字符串或fragment
第二个参数container是一个DOM元素
一个从portal内部触发的事件会一直冒泡至包含React树的祖先,即使这些元素并不是DOM树中的祖先。
如何在React中使用innerHTML?
增加dangerouslySetInnerHTML属性,并且传去对象的属性名叫_html
如:
什么是Redux?
Redux是当今最热门的前端开发库之一。它是JavaScript程序的可预测状态容器,用于整个应用的状态管理。使用Redux开发的应用易于测试,可以在不同环境中运行,并显示一致的行为。
Redux遵循的三个原则是什么?
- 单一事实来源:整个应用的状态存储在单个store中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。
- 状态是只读的:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通JS对象。就像state是数据的最小表示一样,该操作是对数据更改的最小表示。
- 使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于参数值的函数。
Redux的组件:
- Action:这是一个用来藐视发生了什么事情的对象
- Reducer:这是一个去欸的那个状态如何变化的地方
- Store:整个程序的状态/对象树保存在Store中
- View:只显示Store提供的数据
如何在Redux中定义Action?
React中的Action必须具有type属性,该属性指示正在执行的ACTION的类型。必须将它们定义为字符串常量,并且还可以向其添加更多的属性。
解释Reducer的作用
Reducers是纯函数,它他规定应用程序的状态怎样因响应ACTION而改变。Reducers通过接受先前的状态和action来工作,然后它返回一个新的状态。他根据操作的类型确定需要执行哪种更新,然后返回新的值。如果不需要完成任务,它会返回原来的状态。
Redux的Action
Action本质上是JavaScript普通对象。我们约定,action内必须使用一个字符串类型的type字段来表示将要执行的动作。多数情况下,type会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件存放action;
{
type: 'ADD_TODO',
text: 'Build my first Redux app;
}
Action创建函数时生成action的方法
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
Redux中只需把action创建函数的结果传给dispatch()方法即可发起一次dispatch过程
dispatch(addTodo(text));
Redux的Reducer
Reducer指定了应用状态的变化如何响应actions并发送到store的,记住actions只是描述了有事情发生了这一事实,并没有描述应用如何更新state。保持reducer纯净非常重要。永远不要在reducer里做修改传入参数、执行有副作用的操作、调用非纯函数
拆分reducer,每个reducer只负责管理全局state中它负责的一部分。每个reducer的state参数都不一样,分别对应它管理的那部分state数据。redux提供了combineReducers()工具来管理拆分后的reducer,保持其独立性并用于专门处理不同的数据域。combineReducers()所作的只是生成一个函数,这个函数会调用你的一系列reducer,每个reducer根据它们的key来筛选出state中的一部分数据并处理,然后这个生成的函数再将所有的reducer的结果合并成一个大的对象。combineReducers接收一个对象,可以把所有顶级的reducer放到一个独立的文件中,通过export暴露出每个reducer函数,然后import * as reducers得到一个他们名字作为key的object。
Redux的Store
Store是将action和reducers联系到一起的对象。职责:
- 维持应用的state;
- 提供getState()方法获取state;
- 提供dispatch(action)方法更新state;
- 通过subscribe(listener)注册监听器;
- 通过subscribe(listener)返回的函数注销监听器;
Redux应用只有一个单一的store,当需要拆分数据处理逻辑时,应该使用reducer组合而不是创建多个store
import { createStore } from 'redux'
import reducers from './reducers'
let store = createStore(reducers);
Redux应用中数据的生命周期遵循4个步骤:
- 调用store.dispatch(action)
- Redux store调用传入的reducer函数
- 根reducer应该把多个子reducer输出合并成一个单一的state树
- Redux store保存了根reducer返回的完整state树
Redux容器组件
使用connect()方法链接展示组件,传入两个函数mapStateToProps、mapDispatchToProps,执行connect()方法传入展示组件
import { connect } from 'react-redux';
connect(mapStateToProps, mapDispatchToProps)(ComponentName)
function mapStateToProps(state) {
return {
text: state.text
}
}
function mapDispatchToProps(dispatch) {
return {
addTodo: text => dispatch(addTodoAction(text))
}
}
传入store
所有容器组件都可以访问Redux store,所以可以手动监听它。一种方式是把它以props的形式传入到所有容器组件中。但是太麻烦了。因为必须要用store把展示组件包裹一层,仅仅是因为恰好再组件树中渲染了一个容器组件。建议的方式是使用指定的React Redux组件
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from './reducers';
const store = createStore(reducers);
render(
)
异步Action时,使用Redux-thunk
使用redux-thunk库,通过使用指定的middleware,action创建函数除了返回action对象外还可以返回函数。这个action创建函数就成为了thunk。当action创建函数返回函数时,这个函数会被Redux Thunk middleware执行,这个函数不需要保持纯净;它还可以带有副作用,包括执行异步API请求。这个函数可以dispatch action。
使用Redux-thunk
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
const store = createStore(
reducers,
applyMiddleware(thunk)
)
题目
对象自引用
严格模式
arguements
proxy reflect
柯理化 反柯理化
ajax的send
词法作用域,js回收机制
ast
模板语言是如何被渲染成html
vue指令
data在组件和vue实例的区别 mixin react高级方法 electron应用 链表和队列 git的使用 ts的使用 vue的原理 抓包 http请求 请求包 vuex 不同的vuex模块怎么调用 路由的模式 怎么在hash下消除# linux的使用 nginx端口多配置 option是哪个层进行处理的