金沙澳门官网网址_金沙国际登陆

欢迎加入金沙澳门官网网址体验更多不一样的精彩.,金沙国际登陆提供最丰厚回馈!,因为金沙澳门官网网址这里的游戏是多种多样的,为大家打造一个最专业的化的超级五星酒店。

金沙澳门官网网址 > 金沙澳门官网网址 > 0各个击破

原标题:0各个击破

浏览次数:165 时间:2019-12-03

webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习webpack工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本

如何编码解码

webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习webpack工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本

图片 1

编码:var code=encodeURI("原文");
解码:var 原问=decodeURI("code");
用encodeURIComponent代替encodeURI
用decodeURIComponent代替decodeURI
eval 专门执行字符串格式的表达式
var 表达式的结果=eval("表达式")
while循环
语法:
while(循环条件){
循环操作;
}

图片 2

图片 3

三目运算(简化的if...else)

图片 4

一. plugin概述

语法:条件?操作1:操作2
相当于if(条件){
操作1;
}else{
操作2;
}

一. loader综述

loaderwebpack的核心概念之一,它的基本工作流是将一个文件以字符串的形式读入,对其进行语法分析及转换(或者直接在loader中引入现成的编译工具,例如sass-loader中就引入了node-sass将SCSS代码转换为CSS代码,再交由css-loader处理),然后交由下一环节进行处理,所有载入的模块最终都会经过moduleFactory处理,转成javascript可以识别和运行的代码,从而完成模块的集成。

loader支持链式调用,所以开发上需要严格遵循“单一职责”原则,即每个loader只负责自己需要负责的事情:将输入信息进行处理,并输出为下一个loader可识别的格式。

实际开发中,很少会出现需要自己写loader来实现复杂需求的场景,如果某个扩展名的文件无法快速集成到自动化构建工具里,估计很快就会被抛弃了,大家都那么忙是吧。但是了解loader的基本原理和编译器的基本原理却是非常有必要的。

1.1 Plugin的作用

plugin机制是webpack中另一个核心概念,它基于事件流框架tapable,你可以参考浏览器环境中的【DOM事件模型】【SPA模型中的生命周期钩子】或是node环境中的【EventEmitter模块】来理解其作用。plugin系统提供给开发者监听webpack生命周期并在特定事件触发时执行指定操作的能力。

当然,要写一个真正能实现一定功能的插件,你还需要了解CompilerCompilation这两个概念,网上可以找到非常多相关的文章(《webpack-docs/plugin》)。

强化版语法:条件1?操作1:条件2?操作2:条件3?操作3:操作4;
if(条件1){
操作1;
}else if(条件2){
操作2;
}
else{
操作3;
}

二. 如何写一个loader

如果需要编写一个功能完整的loader,建议先到webpack的官方网站浏览一下loader有哪些API,地址:webpack官网-loader API,其中对于编写同步loader异步loader如何跳过loader如何获取options配置项等等都做了非常详细的解释,本篇中不再赘述。

假设现在要实现一个dash-loader,它的功能是加载并处理名称为*.tpl.html的文件,将其变为一个CommonJs模块。也就是说要完成一个如下的基本转换:

转换前的文本:

<div>
    <h3>这里是标题</h3>
    <p>这里是内容</p>    
</div>

转换后的文本:

var str = '<div><h3>这里是标题</h3><p>这里是内容</p></div>';
module.exports = str;

那么webpack.config.js中需要增加如下的配置:

...
module:{
    rules:[{
        test: /.tpl.html$/,
        use:[{
            loader:'dash-loader'
        }]
    }]
}

在项目的node_modules依赖文件夹中新建dash-loader文件夹,并在其中新建一个index.js文件,内容的基本格式为:

//index.js
module.exports = function(source){
    var tpl="";
    source.split(/r?n/).forEach(function(line){
        line=line.trim();
        if(!line.length){
            return;
        }
        //对line进行处理...
        tpl+=line;
    });
    return "var tpl='" + tpl + "'nmodule.exports = tpl"; 
}

最终由dash-loader返回的数据就好像是从某个CommonJs模块中读入的一样了。

1.2 Compiler

从表现上看,Compiler暴露了和webpack整个生命周期相关的钩子,通过如下的方式访问:

//基本写法
compiler.hooks.someHook.tap(...)
//如果希望在entry配置完毕后执行某个功能
compiler.hooks.entryOption.tap(...)
//如果希望在生成的资源输出到output指定目录之前执行某个功能
compiler.hooks.emit.tap(...)

webpack在重要的生命周期节点上都提供了事件钩子,我们可以借此加入一些自定义的功能。我们来编写一个插件,直观地看看webpack中涉及的钩子:

//check-compiler-hooks-plugin.js
const pluginName = 'checkCompilerHooksPlugin';
module.exports = class checkCompilerHooksPlugin {
    apply(compiler){
        //打印出entryOption执行完毕时Compiler暴露的钩子
        for(var hook of Object.keys(compiler.hooks)){
            console.log(hook);
        }
    }
}

可以看到Compiler上可以使用的钩子(当然这种方式看到的钩子和实际触发顺序无关):
图片 5

注意上图中Compiler.hooks暴露的事件钩子中有一个compilation,下一小节将解释它。

for 与 while
1、while循环 一般比较适合用于 不太确定循环次数的时候使用
2、for循环 一般适用于 已经确定了 循环次数的时候使用

三. loader的编译器本质

了解了loader的基本结构,那么loader里到底应该写点什么才能完成代码转换呢?这就涉及到了一个新的概念——编译器(Compiler)。一个基本的编译器,需要经过tokenize,parse,transform,stringify几个核心步骤,它的应用是非常广的,SPA中的virtual-DOM的解析,babel中的ES6语法解析等等,babel的官网曾经推荐过一个非常棒的开源项目(10k+Star),详细讲述了如何一步一步实现一个编译器的,建议感兴趣的同学可以自行学习:

【The-Super-Tiny-Compiler】——https://github.com/jamiebuilds/the-super-tiny-compiler

笔者最近在阅读《你不知道的javascript》一书,发现第一节就在讲述基本的编译原理,是的,你每天都在用的javascript的编译过程,和上面提及的都是一样的,你说要不要学?

1.3 Compilation

Compilation暴露了与模块依赖有关的粒度更小的事件钩子,官方文档中的说法是模块会经历加载(loaded),封存(sealed),优化(optimized),分块(chunked),哈希(hashed)和重新创建(restored)这几个典型步骤,从上面的示例可以看到,compilationCompiler生命周期中的一个步骤,使用compilation相关钩子的通用写法为:

compiler.hooks.compilation.tap('SomePlugin',function(compilation, callback){
    compilation.hooks.someOtherHook.tap('SomeOtherPlugin',function(){
        ....
    })
});

我们仿照上面的方法就可以查看到compilation对象上(compilation事件触发时,在回调函数中取得的引用)暴露的事件钩子。

CompilerCompilation暴露的事件钩子总数超过30个,具体信息可以直接在官方文档直接查询API,在特定的阶段钩入想要添加的自定义功能。想要更好地理解plugin的作用机制,还需要了解webpack的整个生命周期以及事件流框架tapable.

***js内置对象:ES标准中规定的,浏览器厂商已经实现的对象(属性和方法)
11个:String Number Boolean -> 包装类型
Array Date Math RegExp
Error Function Object
Global全局对象 -> 在浏览器中被window代替

【参考】

《如何编写一个loader》

二. 如何写一个plugin

根据webpack官方文档的说明,一个自定义的plugin需要包含:

  • 一个javascript命名函数
  • 插件函数的prototype上要有一个apply方法
  • 指定一个绑定到webpack自身的事件钩子
  • 注册一个回调函数来处理webpack实例中的指定数据
  • 处理完成后调用webpack提供的回调

官网给出了一个基本的结构示例:

//console-log-on-build-webpack-plugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler){
        compiler.hooks.run.tap(pluginName, compilation=>{
           console.log('webpack构建过程开始'); 
        });
    }
}

将其添加到webpack插件中后可以看到运行中触发了传入的回调函数:

图片 6

***包装类型:专门封装原始类型的数据,并提供对原始类型数据的操作方法的对象

四. 实战

在《webpack4.0各个击破(4)——javascript & splitChunks》一文中,我们使用splitChunks功能对初始模块进行代码分割,在为多页面应用模型的html入口插入script标签时遇到了无法自动插入的问题,那么本节我们用一个webpack-dispatch-chunk-plugin来解决一下这个问题。

图片 7

处理的逻辑就是利用html-webpack-plugin暴露的更改资源标签的事件钩子htmlWebpackPluginAlterAssetTags来进行资源处理,此时资源已经离过模块化和代码分割并已经在名称中加入了hash标记,只需要此时过滤掉名称中含有vendors且不包含相应入口名称的新的chunk即可,当然这只是一个基本功能,想要动态实现功能,还需要将上例中checkMap部分变为对Compiler或是Compilation上对应属性的引用,本篇不再赘述。

为什么:原始类型的数据不包含任何属性和功能
本身不能加.调用方法和属性
何时使用:只要试图用原始类型的变量调用属性和方法时,都会自动创建并且调用包装类型对象
何时释放:方法调用后,自动释放!并返回方法执行的结果。

【参考】

[1] webpack之内部运行机制》

为什么null和undefined不能打.:因为没有对应的包装类型

isFinite:判断一个数是否在有效范围内
javascript:infinite 无穷大 5/0
var bool=isFinite(num)
do{
循环体;
}while(条件)

continue:跳过本次循环,继续判断循环条件,结束本次
break:直接退出循环,不再执行循环条件判断,直接结束

7、★★★★★★★★Function

js中函数都是一个对象,函数名都是引用函数对象的变量
创建函数:3种
1、声明方式:function 函数名(参数列表){函数体}
只有声明方式创建的函数,才能被声明提前

鄙视题:js中正确的创建函数的方法
function compare(a,b){return a-b};
var compare=function(a,b){return a-b};
var compare=new Function("a","b","return a-b");

****声明提前(hoist):
在程序执行前
将var声明的变量和function声明的函数
集中到*当前作用域*的开始位置创建
赋值留在原地
强调:只有声明方式定义的函数才能被提前
直接量方式创建的函数,不能提前

只要遇到先使用后创建的,一定实在考声明提前
解决:先把代码转换为提前后的样子,再去判断

****按值传递:两个变量间赋值或将变量作为参数传递时
其实只是将变量中的值复制了一个副本给对本 - 复制副本,各用个的
如果传递的都是原始类型数据的值
修改新变量,原变量不受影响的
如果传递的是引用类型的对象 -- 保存地址值 - 地址值互用,谁用都会发生改变
通过新变量修改对象,等效于直接修改原对象

 

****重载(overload):相同函数名,不同参数列表的多个函数
可在调用时,根据传入参数的不同
自动选择对应的函数执行
为什么:减轻调用者的负担!
问题:js的语法不支持重载!
js不允许多个同名函数同时存在。
如果同时存在,最后定义的函数会覆盖之前所有同名函数
解决:arguments对象
什么是arguments:函数中,专门接受所有传入的参数的对象
arguments是类数组对象:长的像数组的对象
只有两点和数组相同:
1、都可用下标访问每个元素
比如:arguments[1]
2、都有length属性记录参数个数
比如:arguments.length
强调:类数组对象不是数组
几乎所有数组API,都不能使用!
何时创建:在调用函数时,自动创建的函数局部变量!
变相实现了重载

****匿名函数:创建函数时,不适用任何变量引用的函数
何时使用:如果一个函数,只会执行一次!
为什么:节约内存!
因为匿名函数对象,没有变量引用着
用完,自动立刻释放!
如何使用:2种
1、自调:函数定义完,立刻执行!
(function(参数列表){
函数体
})();

2、回调:将函数作为对象,传递给其他函数调用!
arr.sort(function(a,b){return a-b});
//排序后,比较器函数自动释放!

1、闭包
作用域(scope):2种
1、全局作用域
全局变量:随处可用,可反复使用
2、函数作用域
局部变量:只有函数调用时,函数内部才可用
调用结束后,释放
包含:参数和在函数内部声明的变量

***函数的执行原理:
0、程序加载时:
创建执行环境栈,保存函数调用的顺序的数组
首先压入全局执行环境(EC)
全局EC引用全局对象window
window中将要保存全局变量
1、定义时
创建函数对象,封装函数的定义。
在函数对象中定义scope属性,记录函数来自的作用域
全局函数的scope都来window
2、调用前
在执行环境栈中压入新的EC
创建活动对象AO:保存本次函数调用用到的局部变量
在EC中添加 scope.chain 属性引用AO
设置AO的parent为函数的scope引用的对象
3、调用时
变量的使用规则:优先使用局部变量
如果局部没有才去全局找
4、调用完
函数的EC出栈,AO自动释放,局部变量也就自动释放

***闭包:保护一个可反复使用的局部变量的一种词法结构
为什么:全局变量:优点:可重复使用
缺点:随处可用,容易被污染
局部变量:优点:仅内部可用,不会被污染
缺点:一次性

如何实现:3步
1、确定受保护的变量?
2、定义专门使用变量的函数
3、用外层函数,将受保护的变量和操作变量的函数包裹起来
外层函数返回内层函数
鄙视时:
1、判断闭包,找到受保护的变量,确定其值
2、外层函数调用了几次?就创建了几个闭包
受保护的变量就有几个副本
3、同一次外层函数调用,返回的内部函数,都是使用同一个受保护的变量

严格模式的目的
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。

语法和行为改变
全局变量显式声明:一定加var
禁止使用with语句
禁止this关键字指向全局对象
对象不能有重名的属性
函数不能有重名的参数
禁止八进制表示法
不允许对arguments赋值
禁止使用arguments.callee

1、★★★★★★★★Array

3、****数组API:浏览器厂商已经实现的函数,程序员不需要创建,直接使用
0、arr.indexOf() 从前往后查找 返回下标 只能找1个
arr.lastindexOf() 从后往前查找 返回下标 只能找1个
0、arr.every()对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true

var arr=[1,2,3,4,5,6,7,8,9];
var result=arr.every(function(value,index,array){//此函数会接收三个参数:数组项的值、该项在数组中的位置、数组对象本身。
return value>0;
});
alert(result);//true
0、arr.some()对数组中的每一项运行给定函数,如果该函数对任意一项返回 true ,则返回 true 。
var arr=[1,2,3,4,5,6,7,8,9];
var result=arr.some(function(value,index,array){//此函数会接收三个参数:数组项的值、该项在数组中的位置、数组对象本身。
return value>7;
});
alert(result);*/
0、arr.filter()对数组中的每一项运行给定函数,返回该函数结果为 true 的项组成的数组。
var newarr=arr.filter(function(value,index,array){//index索引array数组本身
console.log(index+'----'+value+'-----'+array[index]);
return value>3;
});
console.log(newarr);//[4, 5, 6, 7, 8, 9]
0、arr.map()对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
var newarr=arr.map(function(value,index,array){
return value>5;
return value*=100;
});
console.log(newarr);*/
0、arr.foreach()对数组中的每一项运行给定函数。这个方法没有返回值。
var arr=[1,2,3,4,5,6,7,8,9];
arr.forEach(function(value,index,array){
console.log(index+'----'+value+'-----'+array);
});
0、arr.reduce()迭代数组的所有项,然后构建一个最终返回的值。从数组的第一项开始,逐个遍历到最后。
var arr=[1,2,3,4,5,6,7,8,9,10];
var result=arr.reduce(function(prev,value,index,array){
console.log(prev +'----'+ value);//prev:每一次迭代的结果 value:数组项的值。
return prev+value
},100);//100:迭代的初始值。prev指向它,如果没有这个值prev指向数组的第一项。
alert(result);
1、 toString():不改变原数组,返回新的字符串
1、var str = String(arr);
每个元素值之间用逗号分隔

2、Array.isArray() 判断是否是数组类型

3、arr.concat() 数组拼接 不修改原数组

4、arr.slice():截取 不修改原数组,返回新数组 含头不含尾 省略第二个参数,表示从starti一直获取到结尾
var a = [1,2,3,4,5]; a.slice(0,3); // 返回 [1,2,]

5、arr.join("连接符"); 会改变原数组
可自定义连接符
省略"连接符",等效于String()
固定套路:
1、将单个字符数组拼接为单词(无缝连接)
arr.join("")
问题:频繁的字符串拼接会浪费内存
解决:先将要拼接的子字符串,放入一个数组,最后将数组无缝拼接为字符串

6、arr.splice:删除,添加,替换
强调:直接修改原数组! 返回所有被删除的元素组成的子数组
删除:arr.splice(starti,n)
添加:arr.splice(starti,0,值1,值2...)
强调:原starti位置的元素,以及后续的元素,都会被顺移

替换:arr.splice(starti,n,值1,值2...)
从starti位置开始,删除n个元素,在插入新元素
强调:删除的个数n,和插入新元素个数不必相同

7、翻转数组:arr.reverse(); 改变原数组

8、arr.push() 可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
 9、arr.pop() 从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
10、arr.unshift()方法能够在数组前端添加任意个项 返回新数组的长度。
11、arr.shift() 方法能够移除数组中的第一个项并返回该项,同时将数组长度减 1。

12、arr.sort(); 排序 会改变原数组

问题1:默认只能将所有元素转为string后,在排列
解决:自定义比较器函数

1、必须有两个参数a,b,在被调用时,自动接受正在比较的两个元素值
2、必须返回数字来表示两个值的大小
如果a>b,就返回一个正数
如果a<b,就返回一个负数
如果a=b,就返回0
如何使用比较器:将比较器函数作为参数,传入sort();

问题2:所有算法默认只有升序排列
解决:将比较器中的a-b,换成b-a,让后数-前数;

2、★★★★★★★★String 多个字符组成的*只读*字符数组

1、大小写转换:将字符串中每个英文字母统一转为大写 或 小写
var upper = str.toUpperCase();
var lower = str.toLowerCase();

2、str.charAt(i);等效于 str[i] 获取指定位置的字符:
3、var num=str.charCodeAt(i); 获取指定位置的字符的unicode号:
4、var char=String.fromCharCode(num); 从unicode号翻译为原文:

5、检索字符串:查找关键词的位置。
1、 var i = str.indexOf("关键词",fromi);
从fromi位置开始,查找右侧下一个关键词的位置
第二个参数:表示开始查找的位置
可省略,如果省略,表示从0位置开始
返回值:找到:返回关键词第一个字符的位置的下标 没找到:返回-1

2、从后向前找:var i = str.lastIndexOf("关键词",fromi)
从fromi位置开始,查找左侧下一个关键词的位置
第二个参数:省略,默认从length-1开始
注意:以上两个方法,默认只要第一个就结束了,如果希望找出全部的关键字位置,需要用while循环,
判断只要不等于-1,就可以一直执行

6、截取子字符串:
str.slice(starti,n);支持负数参数,截取到不包含下标n 返回新字符串
str.substring(starti,n)不支持负数参数 返回新字符串
str.substr(starti,n);截取n个,返回新字符串

7、replace替换字符串 string.replace("tmd", "*");返回新字符串

8、str.split() 将字符串以参数分割为数组,返回数组

var str='abcadeafg' str.split('a') //["", "bc", "de", "fg"]

 

4、★★★★★★★★Math

1、取整:3种
1、上取整:只要超过,就取下一个整数
Math.ceil(num);
2、下取整:无论超过多少,都省略小数部分
Math.floor(num);

3、四舍五入取整:够五进位,不够就舍弃
Math.round(num); 返回数字,但只能取整

vs num.toFixed(d); 可以指定小数位数,但返回字符串
自定义round方法:
按任意小数位数四舍五入
返回数字类型
计算机有摄入误差
// function round(num,d){//555.555 2位
// //将num乘以 10 的d次方 55555.49
// num*=Math.pow(10,d);
// console.log(num)
// //用round将num四舍五入 55555
// num=Math.round(num);
// //返回num除以10的d次方 //555.55
// return num/Math.pow(10,d);
// }
// var new1=round(555.555,2);
// console.log(new1);

2、乘方和开方
乘方:Math.pow(底数,幂) 比如:Math.pow(10,2)
开方:Math.sqrt(num) 只能开平方

3、最大值和最小值:返回参数中最大或最小的一个值
Math.max(x,y,z);
Math.min(x,y,z);
获取数组中的最大值或最小值
问题:Math.max()/min() 不支持数组参数!
解决:Math.max.apply(Math,arr);
Math.max.(...arr);

4、绝对值:Math.abs(num);//负数辩正数

5、随机数:Math.random(); 在0~1之间取随机小数
可能取到0,但不可能取到1
在min~max之间取随机整数的公式:
parseInt(Math.random()*(max-min+1)+min);

三角函数复习
圆的方程:
X=a+Math.sin(angle*Math.PI/180)*r
Y=b+Math.cos(angle*Math.PI/180)*r
x,y--盒子的位置
a,b--圆心的位置
angle--角度
r--半径
勾股定理复习
C=Math.sqrt(a*a+b*b)

 

5、★★★★★★★★Date

创建:4种
1、创建一个日期对象,获得客户端当前系统时间
var date = new Date();
2、创建一个日期对象,封装自定义时间
var date = new Date("yyyy/MM/dd hh:mm:ss");
本质:其实日期对象中封装的是1970年1月1日0点至今的毫秒数

3、创建一个日期对象,封装自定义时间
var date = new Date(yyyy,MM-1,dd,hh,mm,ss);
取值返回:MM:0~11 只有月份需要修正
dd:1~31
hh:0~23
ss,mm:0~59

4、复制一个日期对象:
为什么:日期对象的API都直接修改原日期对象
无法获得修改前的日期
何时复制:只要希望起始时间和截止时间同时保存时
都要先复制一个副本,再用副本修改
var endDate = new Date(startDate.getTime());

分量:时间的单位
年月日星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
三句话:
1、命名:年月日星期 都不带s
时分秒毫秒 都以s结尾
2、每个分量,都有一对儿getXXX/setXXX
其中,get负责获取一个分量的值
set负责设置一个分量的值
特殊:Day,没有set方法
3、取值范围:FullYear 就是当前的年份数字
Month:0~11
Date:1~31
Day:0 1 2 3 4 5 6

Hours:0~23
Minutes,Seconds:0~59

计算:
1、两个日期对象相减,结果是毫秒差
2、对每个分量做加减计算,三步:
step1:取出要计算的分量值
var n = date.getXXX();
step2:对取出的分量,做加减
step3:计算后的分量值,设置回去
date.setXXX(n);

简写:date.setXXX(date.getXXX()+或-n)
比如:+3年
date.setFullYear(date.getFullYear()+3);
+4月
date.setMonth(date.getMonth()+4);
+10分钟

日期转字符串:
默认:转为国际日期标准格式
1、转成当地的时间格式
date.toLocaleString(); //返回日期 + 时间

2、转成当地时间格式,仅保留日期部分
date.toLocaleDateString(); //返回日期部分

3、转成当地时间格式,仅保留时间部分
date.toLocaleTimeString(); //返回时间部分

问题:浏览器兼容性,输出格式不一样
解决:自定义格式化方法

3、★★★★★★★★Regexp

什么是:定义字符串中字符出现规则的表达式
何时使用:查找 替换 验证

2、备选字符集:[] 规定*一位*字符可用的备选字符列表
[a-zA-Zu4e00-u9fa5_]{6,14}

强调:1、一个中括号,只能匹配一个字
2、正则表达式不能仅匹配部分规则:必须全部匹配
特殊:1、如果备选字符集中有部分连续字符
可用-省略中间字符
比如:一位数字:[0-9]
一位字母:[a-zA-Z]
一个数字,字母或者下划线:[0-9a-zA-Z_]
一个汉字:[u4e00-u9fa5]

本文由金沙澳门官网网址发布于金沙澳门官网网址,转载请注明出处:0各个击破

关键词:

上一篇:超详细的学习笔记,cli项目安顿文件分析

下一篇:没有了