8、脚本、两种前端、全局变量、三大框架之权限|登录到退出的流程|MVVM|单页优点、环境host、服务器与上传、Linux乌班图VMware、web互联网|ipping|500、yapi、node、npm含Live、自动化工具gulp|webpack|vite、rollup、前端框架工具版本发行时间(含AI豆包、solid、svelte)、神策插件源码解读、aplus插件源码解读(3900行)
附、脚本 1、脚本是一种使用特定描述性语言编写的可执行文件,通常以文本形式保存 2、脚本包含了一系列指令,这些指令由计算机的解释器或脚本引擎执行,用于自动化执行常规任务,如自动化数据处理、网页开发等。 附、两种前端 1、客户端前端(公开前端),客户将数据发给后台,后台将数据存到数据库 2、管理端前端(私密前端),管理向后台索要数据,后台从数据库查找数据 附、全局变量 1、process,node自身的全局变量,node开发服务器webpack使用了这个变量,如 A、process.env,不能在网页中使用 B、process.cwd(),当前Node.js进程执行时的文件夹地址,即当前工作目录,根目录 2、import,ES模块的导入命令,node开发服务器vite利用了import相关的元数据对象来扩展功能,如 A、import.meta.env,能在网页中使用 B、import.meta.glob动态导入模块(可以用通配符),const components = import.meta.glob('../components/*.vue'); 一、三大框架之权限、登录、路由嵌套、状态管理、请求响应拦截、导航守卫、MVVM架构、SPA(单页面应用)优缺点 1、在angular1中, (1)权限:(自动)输入网址或IP,渲染index.html时,执行js,初始化模块,执行到<ui-view>, A、触发$urlRouterProvider.otherwise('/login'),前往登录页,触发前端路由, B、触发$stateChangeStart函数,不向后台发送请求,跳转到登录页。 C、登录成功后,后台通过session或cookie,记录登录状态,前端跳转到首页,遇拦截, D、向后台发送请求,前端获取权限信息保存到本机,建立左侧导航栏和上方选项卡,再次跳转到首页 E、后来刷新浏览器,保存在本机的信息会被清空,前端跳转到当前页,遇拦截,向后台发送请求, F、获取权限信息保存到本机,建立左侧导航栏和上方选项卡。 (2)路由嵌套:<ui-view></ui-view> (3)状态管理:ui-router (4)请求响应拦截:在$http前后,由用户手写,主要用于处理请求参数和返回值 (5)导航守卫拦截:$stateChangeStart等,前端路由变化时触发,主要用于获取用户权限、防止浏览器刷新时前端数据被清除 (6)跳转示例,$state.go('/firstPage') $rootScope.$on('$stateChangeStart', function (event, toState) { myServer.changeState(event, toState); }) var whiteList = ['login','license']; var islogin = getLoginStage(); if (whiteList.indexOf(toState.name) > -1) return; //首页初次渲染时,触发$urlRouterProvider.otherwise('/login'),跳转到登录页,不能往下执行 if (islogin) { if (roles.length === 0) { //向后台发送请求,获取权限表,建立左侧导航栏和上方选项卡 } else { //正常访问,建立上方选项卡 } }else{ $state.go('login') } (7)错误说明 A、一个包含所有权限项的列表 B、项目开始时给所有的路由和页面关联 C、在登录成功后,根据返回的角色,从权限列表中摘出权限项,组成导航 D、错误之处在于,在浏览器手动输入一个权限外的路由,也会出现对应的页面 2、在vue中, (1)权限:(自动)输入网址或IP,渲染index.html时,执行js,初始化模块,执行到<router-view>, A、触发router.beforeEach,前往登录页,触发前端路由,再次触发router.beforeEach,不向后台发送请求,跳转到登录页。 B、登录成功后,后台通过session或cookie,记录登录状态,前端跳转到首页,遇拦截,向后台发送请求, C、前端获取权限信息保存到本机,建立左侧导航栏和上方选项卡,再次跳转到首页。后来刷新浏览器, D、保存在本机的信息会被清空,前端跳转到当前页,遇拦截,向后台发送请求, E、获取权限信息保存到本机,建立左侧导航栏和上方选项卡。 (2)路由嵌套:<router-view></router-view> (3)状态管理:vuex (4)请求响应拦截:service.interceptors,主要用于处理请求参数和返回值 (5)导航守卫拦截:beforeEach等,前端路由变化时触发,主要用于获取用户权限、防止浏览器刷新时前端数据被清除 (6)跳转示例,this.$router.push({ path: '/firstPage' }),next('/firstPage') router.beforeEach((to, from, next) => { var whiteList = ['/login']; var islogin = getLoginStage(); if (islogin) { if (store.getters.roles.length === 0) { //向后台发送请求,获取权限表 } else { //正常访问 document.title = to.meta.title //url变化,浏览器的title也随之变化 } } else { if (to && whiteList.indexOf(to.path) > -1) { next()//不可以拦截 } else { next(`/login`) //首页初次渲染时,从此处跳转到登录页 } } }) 3、在react中, 来源,https://blog.csdn.net/sinat_36728518/article/details/106254395 React-Router4.0以后,路由即组件 (1)权限:(自动)输入网址或IP,渲染index.html时,执行js,初始化模块,渲染组件FrontendAuth,跳转到登录页。 A、登录成功后,后台通过session或cookie,记录登录状态,前端跳转到首页,遇拦截,向后台发送请求, B、前端获取权限信息保存到本机,建立左侧导航栏和上方选项卡,再次跳转到首页。 C、后来刷新浏览器,保存在本机的信息会被清空,前端跳转到当前页,遇拦截,向后台发送请求, D、获取权限信息保存到本机,建立左侧导航栏和上方选项卡。----大概如此 (2)路由嵌套:{React.Children.map}或{props.children}或<header><Route path="/user" component={UsersMenu}/></header> (3)状态管理:react-redux (4)请求响应拦截:service.interceptors,主要用于处理请求参数和返回值 (5)导航守卫拦截:FrontendAuth等,前端路由变化时触发,主要用于获取用户权限、防止浏览器刷新时前端数据被清除 (6)跳转示例,<Redirect to="/login" />;browserHistory.push('/') <Router> <Switch> <FrontendAuth routerConfig={routerMap} /> </Switch> </Router> class FrontendAuth extends Component { constructor(props) { super(props); } render() { const { routerConfig, location } = this.props; const { pathname } = location; const targetRouter = routerConfig.find(function(item){ return item.path === pathname }); const isAuth = targetRouter && targetRouter.auth; const isLogin = sessionStorage.getItem("username"); if (isLogin) { if (pathname === "/login") { return <Redirect to="/" />; } else { if (targetRouter) { //此处建立上方选项卡 return <Route path={pathname} component={targetRouter.component} /> } else { return <Redirect to="/404"/>; } } } else { if(isAuth){ const { component } = targetRouter; return <Route exact path={pathname} component={component} />; }else{ return <Redirect to="/login" />; } } } } export default FrontendAuth; 4、从登录到退出的简易流程 附、需要重新登录的两种情形 A、后台判断登录过期 B、前端判断登录标志缺失,比如退出时清空了登录标志 (1)登录: A、初次访问服务器,返回#前面的内容,即index.html,加载style和cript标签里的静态文件, B、初始化状态管理插件的数据,获取登录者身份标志localStorage.getItem(TokenKey)失败,跳到登录页, C、登陆成功,存储登录者身份标志localStorage.setItem(TokenKey, JSON.stringify(token));根据权限列表,生成导航 (2)跳转:用状态管理插件存储“临时数据” (3)刷新: A、访问服务器,返回#前面的内容,即index.html,加载style和cript标签里的静态文件, B、初始化状态管理插件的数据,即清除“临时数据”,获取登录者身份标志localStorage.getItem(TokenKey)成功, C、获取权限列表(有可能返回登录过期,跳到登录页),生成导航 (4)退出:清除登录者身份标志,localStorage.removeItem(TokenKey),跳到登录页 5、登录前滑块验证的逻辑 (1)点击验证,向后台发送请求,后台返回带孔图片、填孔图片、相对坐标,前端根据坐标,把2张图片叠放在弹窗上 (2)前端拖动图块或划块,放手,将移动距离发给后台, (3)后台告诉前端验证的成功标志,前端隐藏弹窗;后台告诉前端验证失败,前端再次向后台请求图片,直到验证成功 (4)前端向后台发送用户名、密码、成功标志,后台告诉前端登陆成功,前端跳往首页 (5)图片代码 <img :src="'data:image/png;base64,' + verify.backImgBase" alt=""/> <img :src="'data:image/png;base64,' + verify.blockBackImgBase" alt=""> 附1、MVVM (1)M:Model,数据模型,前端定义的初始数据 (2)V:View,页面视图,前端定义的页面组件 (3)VM:ViewModel,视图模型,通过数据绑定将数据模型渲染到页面视图,将视图转化成模型,通过事件监听,更改数据模型并重新渲染页面视图 (4)MVC,手动同步;MVVM,自动同步 附2、SPA(单页面应用)优缺点 优点: (1)对服务器压力小 (2)前后端职责分离,前端进行交互逻辑,后端负责数据处理 缺点 (1)初次加载耗时多 (2)前进后退路由管理,需要自己建立堆栈管理 (3)SEO难度较大:由于所有的内容都在一个页面中动态替换显示 二、环境 1、生产环境:服务器 2、开发环境 (1)前端:node (2)后台:ubuntu 3、window环境下的上传工具 (1)MobaXterm (2)xshell 4、ubuntu环境下的上传命令 scp -r localfile.txt username@192.168.0.1:/home/username/ (1)scp是命令,-r是参数 (2)localfile.txt 是文件的路径和文件名 (3)username是服务器账号 (4)192.168.0.1是要上传的服务器ip地址 (5)/home/username/是要拷入的文件夹路径 三、服务器 1、安装服务器系统 (1)给服务器--插上--系统U盘 (2)重启服务器:拔、插服务器电源(服务器自动安装系统,安装成功时,服务器自动断电) (3)从服务器--拔掉--系统U盘 (4)再次重启服务器:拔、插服务器电源 2、登陆服务器 (1)用“网线”把“我的电脑”和“服务器”进行物理连接 (2)在“终端工具”上,用“终端工具”的用户名和密码登录“终端工具” (3)在“终端工具”上,用“服务器”的用户名和密码登录“服务器” 3、给服务器配置IP (1)root@CyOS:/root# ifconfig mgmt0 192.168.10.156(给接口配置IP。它有65535个端口,ifconfig(配置))mgmt0(接口)) (2)按enter键,IP配置成功 4、启动服务器 (1)root@CyOS:/root# cd /usr/local/audit-web (2)root@CyOS:/usr/local/audit-web# python manage.py runserver 5、辅助步骤 (1)Ctrl+C(退出进程) (2)root@CyOS:/usr/local/audit-web# pkill -f python -9(杀死python进程,出现Address already in use时,需要用到这个命令) (3)root@CyOS:/usr/local/audit-web# pkill -f uwsgi -9(杀死名为uwsgi的进程,-f匹配所有参数列表,-9强制) (4)root@CyOS:/usr/local/audit-web# iptables -P INPUT ACCEPT(关闭防火墙,P INPUT ACCEPT均大写) (5)root@CyOS:/usr/local/audit-web# ps aux|grep python(查看活进程,aux显示所有进程和进程状态,grep在这些里搜索) (6)root@CyOS:/root# ifconfig(显示或设置网络设备)///////////////////////// (7)root@CyOS:/root# cat /usr/local/etc/suricata/version.autogen(查看当前版本) 6、通过CMD命令窗口,给本地服务器更新文件的流程 (1)window + r;cmd (2)ssh root@172.18.58.30,root为登录名 (2-1)SSH(Secure Shell,安全外壳)之所以能够保证安全,原因在于它采用了公钥加密。整个过程是这样的: (2-2)远程主机收到用户的登录请求,把自己的公钥发给用户。 (2-3)用户使用这个公钥,将登录密码加密后,发送回来。 (2-4)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。 (3)输入密码 (4)wget -O /my.sh "http://192.168.80.152:3000/audit/shell?branch=develop-2.0.8&command=gulp build" && chmod +x /my.sh && /my.sh && rm -f /my.sh (4-1)-O /my.sh:将下载的文件存储在根目录下并重命名为“my.sh” (4-2)chmod +x /my.sh:将执行权限赋给根目录下的“my.sh” (4-3)/my.sh:运行根目录下“my.sh” (4-4)rm -f /my.sh:删除根目录下“my.sh” (5)Windows运行CMD常用命令:https://blog.csdn.net/gaofengyan/article/details/89447293 (6)在WINDOWS操作系统里执行CMD命令,可以模拟出DOS操作系统。 7、负载均衡,来源https://blog.csdn.net/yiXin_Chen/article/details/123158091 (1)正向代理,客户端访问代理服务器,前者指定目标服务器,代理服务器代理的是客户端,服务器不知道客户端的地址 (2)反向代理,客户端访问代理服务器,后者指定目标服务器,代理服务器代理的是服务器,客户端不知道服务器的地址 (3)负载均衡,反向代理时,代理服务器把不同的用户访问,指派给不同的目标服务器处理,避免某一目标服务器负载过重 (4)Nginx,性能非常好的反向代理服务器,用来做负载均衡,可以在Linux系统中运行 四、本地文件上传至服务器(以MobaXterm为例) 1、基本快捷键 (1)复制:Ctrl + Insert (2)粘贴:Shift + Insert 2、连接服务器 (1)点击左上方Session (2)点击左上方SSH (3)Remote host *:172.18.57.88;勾选方框;Specify username:root;点击OK; (4)password:cy888888;回车 (5)是否保存密码:是 3、关闭服务 (1)root@OS:~# ls (2)lsof -i:5000 (3)kill 31096 4、开启服务 (1)python /usr/local/process_monitor/process_monitor.py 5、重启服务 (1)pkill -9 uwsgi3 6、上传 (1)在箭头下方的输入框输入:/usr/local/audit-html/template/;enter (2)点击绿色向上虚线箭头,选择要上传的文件,上传 (3)重启服务器,pkill -9 uwsgi3 7、进入文件夹 (1)cd /usr/local/audit-html/template/ 附、Portable之本地替换服务器文件的步骤,以景宝为例 (1)下载软件并登录 (2)进入到相关文件夹下:root@OS:~# cd /usr/local/audit-html/template/ (3)删除原文件:rm -rf audit-with-reports.html (4)上传新文件:rz (5)关闭服务:pkill -9 uwsgi (6)重启服务:uwsgi3 /usr/local/audit-web/WSGI/uwsgi.ini (7)实时观察:tail -f /data/flask_web/logs/uwsgi.log 附、Jenkins,基于Java开发的一种持续集成工具 (1)软件版本发布/测试 (2)监控外部调用 五、Linux命令与目录 1、清除终端内容 (1)ctr+l:终端内容滚到顶部上面 (2)clear:终端内容滚到顶部上面 (3)reset:清除终端内容 2、ps:显示系统进程 (1)ps -ef:用标准格式显示进程,如ps -ef|grep python (2)ps aux:用BSD格式显示进程,如ps aux|grep python 3、cd:进入目录 (1)cd /:进入系统根目录 (2)cd ~:进入用户根目录 (3)cd ../:切换到上一级目录 4、ls:显示本目录下之内容 (1)-a 显示所有文件及目录 (. 开头的隐藏文件也会列出) (2)-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出 (3)-r 将文件以相反次序显示(原定依英文字母次序) (4)-t 将文件依建立时间之先后次序列出 (5)-A 同 -a ,但不列出 "." (目前目录) 及 ".." (父目录) (6)-F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/" (7)-R 若目录下有文件,则以下之文件亦皆依序列出 5、ls -l运行结果说明 示例:drwxr-xr-x 1 qiancheng qiancheng 54713 11月 19 13:08 0.js (1)文件属性:drwxr-xr-x。d(-文件,d目录)rwx(用户权限)r-x(组用户权限)r-x(其他用户权限);rwx-的含义:r可读,w可写,x可执行,-没有权限 (2)文件硬链接数量:1 (3)所有者:qiancheng(user) (4)所属用户组:qiancheng(group) (5)文件大小:54713 (6)修改时间:11月 19 13:08 (7)文件名:0.js(Filename) 6、关于目录的命令 (1)目录创建:mkdir dir (2)目录改名:mv/cp oldDir newDir(不存在); (3)目录复制:mv/cp oneDir newDir(存在); (4)目录查找:find dir -type d; (5)目录清空:A、根目录清空:rm -rf /*;B、本目录清空:rm -rf *;C、子目录清空:rm -rf dir/*; (6)目录删除:rm -rf dir; 7、关于文件的命令 (1)文件创建:A、touch aaa.js;B、cat >> aaa.js (2)文件改名:mv aaa.js bbb.js (3)文件复制:cp aaa.js bbb.js (4)文件查找:A、find dir -type f;B、find dir -name "*.js" (5)文件清空:A、> aaa.js;B、cat /dev/null > aaa.js (6)文件删除:rm aaa.js 8、文件内容查看 (1)nl:显示所有文件内容,并输出行号 (2)cat:由第一行开始显示文件内容 (3)tac:从最后一行开始显示文件内容 (4)less:一页一页的显示文件内容,可以用上下键上下翻页 (5)more:一页一页的显示文件内容,可以往enter键向下翻页 (6)head:只看头几行 (7)tail:只看尾巴几行 9、grep:与字符串相关的搜索 (1)grep "grep" html/*.js (2)grep "g.\{0,4\}p" html/*.js (3)grep -i "grep" html/*.js (忽略大小写) (4)grep -w "grep" html/*.js (搜索整个词) (5)grep -n "grep" html/*.js (显示行号) (5)grep -l "grep" html/*.js (只显示文件名) (6)grep -r "grep" html (递归搜索当前目录及其子目录的全部文件) (7)grep -v "grep" html/*.js (显示不匹配字串的行) (8)grep -c "grep" html/*.js (统计匹配的行数) (9)grep -B 2 "grep" html/*.js (显示之前2行,A后,C前后各) (10)grep,全局正则表达式输出,global regular expression print */ 10、echo:输入字符串 (1)覆盖写入字符串(无引号):echo "字符串" > 1.js (2)覆盖写入字符串(有引号):echo "\"字符串\"" > 1.js (3)转义覆盖写入字符串(有引号):echo -e "\"这\n是\n换\n行\"" > 1.js;e开启转义,\n换行,\c:不换行 (4)追加写入字符串:echo "字符串" >> 1.js 11、关机 (1)shutdown:关机 (2)shutdown -t 2:2秒后关机 (3)shutdown -h now:立刻关机 (4)shutdown -h 20:00:20:00关机 (5)shutdown -h +10:10分钟后关机 (6)shutdown -r now:系统立刻重启 (7)shutdown -r +30:30分钟后重启 (8)halt:关机 (9)poweroff:关机 (10)init 0:关机 (11)reboot:重启 12、压缩与解压(解压导出需要加参数) (1)压缩:tar -czvf file.tar ./aaa/file1;zip html.zip ./aaa (2)解压:tar -xzvf file.tar -C ./aaa/file2;unzip -d ./aaa file.zip (3)tar参数之-c建立新的备份文件 (4)tar参数之-x从备份文件中还原文件 (5)tar参数之-z通过gzip指令处理备份文件 (6)tar参数之-v显示指令执行过程 (7)tar参数之-f指定备份文件 (8)unzip参数之-d指定文件解压缩后所要存储的目录 附、其它 (1)打开终端:Ctrl+Alt+T (2)查看当前目录:pwd 13、Linux一级目录: 来源:https://www.runoob.com/linux/linux-system-contents.html (1)srv:存放着-服务启动-之后-需要提取-的数据 (2)root:存放着-系统管理员-的主目录 (3)sbin:存放着-系统管理员-使用的-系统管理程序 (4)home:存放着-用户-的主目录 (5)bin: 存放着-用户-的常用命令 (6)boot:存放着-系统启动-时使用的文件 (7)run:存放着-系统启动-以来的信息 (8)etc:存放着-系统管理-所需要的文件 (9)dev:存放着-外部硬件-的设备,包括硬盘,U盘,光驱,串口,打印机等等 (10)media:存放着-自动识别-的设备,比如U盘下的ubuntu (11)mnt:存放着-临时挂载的-别的文件,比如光驱,进入后可以查看光驱内容 (12)tmp:存放着-临时文件 (13)var:存放着-经常被修改-的文件 (14)opt:存放着-主机-额外安装-的软件,比如搜狗输入法 (15)lib:存放着-动态连接共享库。类似于Windows 里的 DLL 文件 (16)proc:存放着-当前内核-运行状态-的文件 (17)selinux:防火墙,存放selinux相关的文件的 (18)usr:存放着-应用程序。类似于 windows 下的 program files 目录,unix shared resources(共享资源) 的缩写 (18-1)/usr/bin:系统用户使用的应用程序 (18-2)/usr/sbin:超级用户使用的比较高级的管理程序和系统守护程序 (18-3)/usr/src:内核源代码默认的放置目录 附、Linux文件的时间属性 (1)atime:文件内容查看时间,Access time,使用more、cat对该文件进行查看时,atime将更新,ls -lu (2)mtime:文件内容修改时间,Modify time,使用vi、vim对文件进行修改后保存,mtime将更新,ls -l (3)ctime:文件属性变更时间,Change time,文件名、内容、大小、权限、所属组等改变时,ctime将更新,ls -lc 六、Linux,ubuntu和VMware 1、三者之间的关系 (1)Linux是一个操作系统; (2)Ubuntu是一个以桌面应用为主的Linux操作系统 (3)VMware是一个在安装的过程中可以导入ubuntu的虚拟主机 (4)在ubuntu里,应该用Linux命令来操作里面的文件 (5)安装步骤;点击VMware.exe文件直到结束-->点击桌面VMware图标-->点击“创建新的虚拟机”-->安装依赖(最后一步,耗时约40分钟) (6)4步完全卸载VMware。A、关闭VMware;B、应用卸载-->vmware-->修改-->下一步-->删除;C、C:\Program Files (x86)下删除VMware;D、C:\Users\用户名\Documents下删除Virtual Machines,内含ubuntu (7)层级关系:Unix-->Linux-->Debian(得便,自由操作系统)-->Ubuntu 2、Linux的vim编辑器的安装和使用 (1)安装:sudo apt install vim (2)显示文件内容:vim 路径,如sudo vim ~/.bashrc (3)命令模式。i 切换到输入模式;: 切换到底线命令模式;x 删除当前光标所在处的字符。 (4)输入模式。ESC 退回命令模式。 (5)底线命令模式。q 退出vim,回到(2)执行前的状态;w 保存文件;字母后面加! 强制。 (6)退出vim。输入模式ESC--命令模式:--底线命令模式q! 3、ubuntu的常见命令和依赖包安装 (1)获取超级用户权限:sudo su (2)重启虚拟机:sudo reboot (3)本地安装:sudo dpkg -i package,“dpkg”是“Debian”的包管理器 (4)本地安装:sudo apt install package,“apt”是“Debian”和“Ubuntu”的包管理器 (5)自由安装:wget http://url (6)层级关系:Unix-->Linux-->Debian(得便,自由操作系统)-->Ubuntu 4、ubuntu实现路径补全 (1)创建文件:touch ~/.pythonrc (2)添加如下内容: import rlcompleter, readline readline.parse_and_bind('tab:complete') (3)在home目录下,.bashrc文件末尾追加如下内容:export PYTHONSTARTUP=~/.pythonrc (4)更新环境变量:source ~/.bashrc 七、在Ubuntu16.04(乌班图)里,安装插件,如搜狗输入法、谷歌浏览器、微信、时间同步、gulp 1、安装简体中文输入法: (1)设置-系统设置-语言支持-安装-密码-确认-(安装fcitx,耗时约10分钟) (2)语言-安装/卸载语言-(勾选)中文简体-应用-密码-确认-(安装字体,耗时约10分钟) (3)区域格式-汉语中国-语言-(汉语中国)拉到第一位-(键盘输入法系统)fcitx-关闭 (4)删除包sudo apt-get remove fcitx-ui-qimpanel 附1:Fcitx是(Free Chinese Input Toy for X)的英文缩写,可为支持 XIM 的 X 应用程序提供输入服务。 可以输入UTF-8编码中的汉字。 2、安装搜狗输入法 (1)下载搜狗输入法安装包http://pinyin.sogou.com/linux,进入下载目录cd Downloads (2)安装搜狗输入法sudo dpkg -i sogoupinyin_2.4.0.3469_amd64.deb (3)重启虚拟机sudo reboot (4)文字图标-配置当前输入法-(搜狗输入法个人版)调至第一个选项 3、1和2安装过程中或结束后,可能遇到的问题及解决 (1)问题1: 问题:updating cache ; waiting for apt-get to exit 解决:sudo fuser -vki /var/lib/apt/lists/lock (2)问题2: 问题:由于找不到vcruntime140_1.dll,无法继续执行代码重新安装程序可能会解决此问题 解决:下载文件vcruntime140_1.dll,复制到32/64位版本电脑的的C:\Windows\system64/32下 (3)问题3: 问题:搜狗输入时出现乱码 解决:killall fcitx (4)问题4: 问题:Windows和Ubuntu16之间不能复制粘贴 解决:sudo apt-get autoremove open-vm-tools ; sudo apt-get install open-vm-tools-desktop ; Ctrl+c ; sudo reboot 4、安装谷歌浏览器 (1)sudo wget http://www.linuxidc.com/files/repo/google-chrome.list -P /etc/apt/sources.list.d/ (2)wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add - (3)sudo apt-get update (4)sudo apt-get install google-chrome-stable (5)/usr/bin/google-chrome-stable (6)在屏幕左侧的图标上,右键“锁定到启动器” 5、安装微信(可登陆,2021年11月11日) (1)安装 deepin-wine:wget -O- https://deepin-wine.i-m.dev/setup.sh | sh 以下主要来源:https://blog.csdn.net/sinat_39369871/article/details/110095705 (2)下载微信安装包:http://packages.deepin.com/deepin/pool/non-free/d/deepin.com.wechat/,点击最后一个 (3)一次运行下列命令(复制以下内容,粘贴到终端,按回车) #!/bin/bash mkdir ./deepintemp cd ./deepintemp wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-wine_2.18-22~rc0_all.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-wine32_2.18-22~rc0_i386.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-wine32-preloader_2.18-22~rc0_i386.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-helper/deepin-wine-helper_1.2deepin8_i386.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-plugin/deepin-wine-plugin_1.0deepin2_amd64.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-plugin-virtual/deepin-wine-plugin-virtual_1.0deepin3_all.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-uninstaller/deepin-wine-uninstaller_0.1deepin2_i386.deb wget http://packages.deepin.com/deepin/pool/non-free/u/udis86/udis86_1.72-2_i386.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-fonts-wine_2.18-22~rc0_all.deb wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-libwine_2.18-22~rc0_i386.deb wget https://packages.deepin.com/deepin/pool/main/libj/libjpeg-turbo/libjpeg62-turbo_1.5.1-2_amd64.deb --no-check-certificate wget https://packages.deepin.com/deepin/pool/main/libj/libjpeg-turbo/libjpeg62-turbo_1.5.1-2_i386.deb --no-check-certificate sudo dpkg --add-architecture i386 sudo apt update sudo dpkg -i *.deb sudo apt install -fy rm -vfr ./deepintemp (4)安装微信 sudo dpkg -i deepin.com.wechat_2.6.8.65deepin0_i386.deb 6、解决微信不能输入中文的问题 (1)cd /opt/deepinwine/tools/ (2)sudo chmod 777 run.sh #文件默认为只读,修改权限 (3)vim run.sh #编辑脚本,加入以下内容: export GTK_IM_MODULE="fcitx" export QT_IM_MODULE="fcitx" export XMODIFIERS="@im=fcitx" (4)重启微信,切换为系统自带的fcitx输入法即可输入中文,比如搜狗输入法。 7、系统时间跟本地时间同步 (1)sudo timedatectl set-local-rtc 1 (2)sudo timedatectl set-timezone Asia/Shanghai (3)data(查看系统时间,可以不执行) 8、全局安装gulp 在ubuntu里,全局安装的gulp才能运行gulp任务,npm全局安装gulp会出错,以下用cnpm安装 (1)sudo npm install -g cnpm -registry=https://registry.npm.taobao.org (2)sudo cnpm install -g gulp (3)gulp 八、web相关 1、专业术语 (1)网络,web、net、network (2)超链接,hyperlink,从一个网页指向另一个网页的链接关系 (3)超文本,Hypertext,是超链接的载体;<a href='url'>文本或图像</a> (4)超媒体,hypermedia,是超文本和多媒体(文本、声音、图像)在信息浏览环境下的结合 2、互联网,internet,是通过“TCP/IP协议”互联的庞大网络,该协议包含4个层级 来源,https://baike.baidu.com/item/%E4%BA%92%E8%81%94%E7%BD%91/199186 来源,https://baike.baidu.com/item/TCP/IP%E5%8D%8F%E8%AE%AE/212915 (1)应用层,使用不同协议,如Http、FTP、SMTP、Telnet,来加密、解密、格式化数据 (2)传输层 (3)网络层 (4)网络接口层 注、TCP/IP协议,Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议,是指能够在多个不同网络间实现信息传输的协议簇 注、HTTP,Hypertext Transfer Protocol,超文本传输协议,是一个简单的“请求-响应”协议,它不仅保证正确传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等 3、万维网,web,是通过“客户机-服务器”这种方式,连接的互联网,应用层使用http协议 (1)是一种基于超文本和超文本传输协议(HTTP)的、全球性的、动态交互的、跨平台的分布式图形信息系统 (2)是建立在Internet上的一种网络服务,为浏览者提供了图形化的直观界面 (3)其中的文档和超链接将Internet上的信息节点组织成一个互为关联的网状结构 (4)包含浏览器、URL、HTTP、HTML 注、万维网,World Wide Web,全球广域网,简称 WWW、3W、Web 4、互联网发展史 来源1,https://baike.baidu.com/item/%E4%BA%92%E8%81%94%E7%BD%91/199186?fr=ge_ala 来源2,https://baike.baidu.com/item/www/109924?fr=ge_ala 来源3,https://baike.baidu.com/item/web/150564?fr=ge_ala (1)1969年,互联网(internet,因特网),诞生于美国,首先用于军事连接 (2)1973年-1984年,TCP/IP协议被开发出来,并成为多数计算机共同遵守的一个标准 (3)1989年,万维网(Web),在欧洲提出 (4)1990年,在一台计算机实现 (5)1991年,在其他计算机实现,并正式发布 (6)1994年,Web1.0开始,主要特征是大量使用静态的HTML网页来发布信息,并开始使用浏览器来获取信息,这个时候主要是单向的信息传递 (7)2004年,Web2.0开始,更加注重交互性,与网络服务器之间交互,不同用户之间的交互,不同网站之间信息的交互 (8)2014年,Web3.0开始,以网络化和个性化为特征,可以提供更多人工智能服务,用户可以实现实时参与 5、用户与内外网安全连接 (1)外网,iNode智能客户端 A、iNode智能客户端是H3C(华三通信)自行设计开发的基于Windows的多业务接入客户端软件,提供802.1x、Portal(门户网站)、VPN等多种认证方式, B、可以与H3C以太网交换机、路由器、VPN网关等网络设备共同组网,实现对宽带接入、VPN接入和无线接入的用户认证, C、是对用户终端进行身份验证、安全状态评估以及安全策略实施的主体,可以按照企业接入安全策略的要求,实现基于角色/身份的权限和安全控制。 (2)内网,MotionPro安全隧道 A、企业用户服务器安全保护软件 B、这是一种VPN软件,Virtual Private Network,虚拟专用网络 C、站点地址1,tech.vpn.cntv.cn;站点地址2,202.18.19.157 D、motion,运动 (3)本机,奇安信天擎 A、致力于一体化“终端安全”解决方案的终端安全管理系统 B、帮助政企客户准确识别、保护和监管终端,并确保这些终端在任何时候都能可信、安全、合规地访问数据和业务 C、高性能病毒查杀、漏洞防护、主动防御引擎,深度融合威胁情报、大数据分析和安全可视化 D、帮助政企客户构建持续有效的终端安全 6、hosts文件 来源,https://blog.csdn.net/qq_41169544/article/details/123895563 附、2个地址, a、IPv4地址,是本机“供其他设备访问”的ip地址,绑在“⽹络接⼝”上,通常“本机的ip地址”就是这个地址 在他人的浏览器地址栏输入,我的IPv4地址+我的端口号,如,http://172.20.69.43:3000,可以访问我的本地开发服务器 b、环回地址,是本机“向自身发送通信”的ip地址,写在“hosts文件”里,即C:\Windows\System32\drivers\etc\hosts, 环回地址(Loopback Address)范围为,127.0.0.0到127.255.255.255 (1)作用:将域名映射到IP地址的纯文本文件 (2)示例 # Copyright (c) 1993-2009 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # # This file contains the mappings of IP addresses to host names. Each # entry should be kept on an individual line. The IP address should # be placed in the first column followed by the corresponding host name. # The IP address and the host name should be separated by at least one # space. # # Additionally, comments (such as these) may be inserted on individual # lines or following the machine name denoted by a '#' symbol. # # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host # localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost 10.51.33.7 gzx.zhixue.com //以上,把-联调服务器的域名-指向-联调服务器的ip 127.0.0.1 test.cctv.com //以上,把-联调服务器的域名-指向-本机的ip //a、浏览器输入http://localhost:3000,无效, //b、浏览器输入http://127.0.0.1:3000,无效, //c、浏览器输入http://test.cctv.com:3000,有效 7、ipconfig命令的执行与说明 (1)示例1,未联网 PS C:\Users\Haier\Desktop> ipconfig Windows IP 配置 以太网适配器 本地连接* 9: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 以太网: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::d5f:3ca4:75b7:cbe7%16 自动配置 IPv4 地址 . . . . . . . : 169.254.17.146 子网掩码 . . . . . . . . . . . . : 255.255.0.0 默认网关. . . . . . . . . . . . . : PS C:\Users\Haier\Desktop> (2)示例2,已联网 PS C:\Users\Haier\Desktop> ipconfig Windows IP 配置 以太网适配器 本地连接* 9: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : PPP 适配器 Array Networks SSL VPN: 连接特定的 DNS 后缀 . . . . . . . : IPv4 地址 . . . . . . . . . . . . : 10.65.205.89 //外网给的 子网掩码 . . . . . . . . . . . . : 255.255.255.255 默认网关. . . . . . . . . . . . . : 以太网适配器 以太网: 连接特定的 DNS 后缀 . . . . . . . : example.org 本地链接 IPv6 地址. . . . . . . . : fe80::d5f:3ca4:75b7:cbe7%16 IPv4 地址 . . . . . . . . . . . . : 172.20.69.43 //路由器分配的 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 172.20.69.1 PS C:\Users\Haier\Desktop> (3)这是查看“IP配置”的命令 A、找到界面: a、window + r b、cdm + enter B、命令用途:根据“是否有PPP适配器信息”判断,本主机是否联网,如iNode未登录,无法访问百度 附1、IPv4地址 A、PPP适配器,IPv4地址,外网给的,子网掩码255.255.255.255 B、以太网适配器,IPv4地址,路由器分配的,子网掩码255.255.255.0 C、IP地址分类 a、A类,前1段为网络地址,后3段为主机地址,IP地址范围,0.0.0.0--127.255.255.255,最大主机数,16777214;默认子网掩码,255.0.0.0 b、B类,前2段为网络地址,后2段为主机地址,IP地址范围,128.0.0.0--191.255.255.255,最大主机数,65534;默认子网掩码,255.255.0.0 c、C类,前3段为网络地址,后1段为主机地址,IP地址范围,192.0.0.0--223.255.255.255,最大主机数,254;默认子网掩码,255.255.255.0 d、公司IP地址一般为C类,D类和E类为未来使用 附2、子网掩码(Subnet Mask) 来源,https://blog.51cto.com/u_15329201/3403517 A、作用 a、把IP地址可以分为“网络标识”和主机标识两部分 b、在一个“网络标识”下的计算机,可以直接互通 c、不在一个“网络标识”下的计算机,要通过网关(Gateway)才能互通 d、指明IP地址是在局域网上,还是在广域网上 B、子网(Subnet),公司网管 a、根据公司IP地址的类别和默认子网掩码 b、生成子网IP,分发给各个开发者使用 C、使用情形1,开发时, a、前端通过代理服务器拼接后端ip(本条去掉下面内容依然成立), b、本机根据本机ip和子网掩码判断,目标主机是否在本地子网中 c、如果目标主机在本地子网中,则直接发送即可 d、如果目标主机不在本地子网中,则将该信息送到默认网关/路由器,由路由器将其转发到其他网络中,进一步寻找目标主机 D、使用情形2,浏览器地址栏域名访问时, a、首先,系统自动从hosts文件(将域名映射到IP地址的纯文本文件)中寻找对应的IP地址, b、如果能找到,则结合子网掩码判断该IP属于局域网还是远程网,然后访问该网 c、如果没找到,系统会将网址提交DNS域名解析服务器,解析出IP地址,然后访问该网 附3、默认网关 A、default gateway B、网关(Gateway),通常是一个路由器,子网与外网连接的设备 8、ping命令的执行与说明 (1)示例1,该主机不可达 C:\Users\Haier>ping 192.0.0.0 正在 Ping 192.0.0.0 具有 32 字节的数据: 请求超时。 请求超时。 请求超时。 请求超时。 192.0.0.0 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 0,丢失 = 4 (100% 丢失), C:\Users\Haier> (2)示例2,该主机可达 C:\Users\Haier>ping www.baidu.com 正在 Ping www.a.shifen.com [180.101.50.242] 具有 32 字节的数据: 来自 180.101.50.242 的回复: 字节=32 时间=21ms TTL=48 来自 180.101.50.242 的回复: 字节=32 时间=21ms TTL=48 来自 180.101.50.242 的回复: 字节=32 时间=21ms TTL=48 来自 180.101.50.242 的回复: 字节=32 时间=21ms TTL=48 180.101.50.242 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 21ms,最长 = 21ms,平均 = 21ms C:\Users\Haier> (3)这是测试“网络连通性”的命令 A、找到界面: a、window + r b、cdm + enter B、命令用途:根据“Ping统计信息”判断,该主机是否可达,如MotionPro未连接,无法从开发后台获取数据 C、命令原理:它通过向指定的网络地址发送一定长度的数据包,并等待接收返回的数据包来检测网络的连通性 附1、命令示例 A、ping www.baidu.com,另一台主机的域名 B、ping 192.168.0.101,另一台主机的IP地址 C、ping localhost,测试本地网络连接是否正常 D、ping 127.0.0.1,测试本地网络连接是否正常 9、500错误 (1)现象:前端带着base64数据向后台发出了post请求,后台没有请求记录,前端收到500 (2)原因:后台服务器已满 九、yapi 1、下载 (1)nodejs(7.6+) (2)mongodb(2.6+),默认安装在C盘,应改在D盘,且路径中的目录名不能有空格。 2、安装步骤及注意事项 (1)卸载nodejs, (2)删除npm,C:\Users\XXXXX\AppData\Roaming\npm (3)安装node,遇到“Custom Setup”时,选择“npm package manager” (4)安装mongodb,遇到“Installing MongoDB Compass”时,去掉默认的勾选 (5)每次重新搭建yapi之前,需要删除my-yapi目录、卸载并安装1次mongodb 3、安装并运行yapi (1)安装yapi脚手架,npm install -g yapi-cli --registry https://registry.npm.taobao.org (2)搭建yapi项目,yapi server,想把yapi搭建在哪个目录下,就在哪个目录下运行此命令。 (3)访问yapi项目,http://127.0.0.1:9090/ (4)配置并连接数据库,即填写公司名称、点击开始部署 (5)启动yapi,在my-yapi目录下运行node vendors/server/app.js --另、在谷歌浏览器上装corss-request插件后,可以在在yapi上发送请求 4、登录 (1)http://127.0.0.1:3000(仅限自己)或者http://172.18.10.12:3000(自己和别人) (2)登录/注册,选登录 (3)账号名:"admin@admin.com", (4)密码:"ymfe.org" (5)登录 5、yapi的层级 (1)分组 (2)项目 (3)分类 (4)接口 十、node(应用程序,能让JS在服务端运行,也能让服务器搭建在本地) 附、node简介:(搜索node版) (1)2009年5月,Ryan Dahl发布Node.js,它基于Chrome V8引擎的JS运行环境,使用了一个事件驱动、非阻塞式I/O模型 (2)node.js让JS成为服务端的一种脚本语言 (3)node.js让前端可以安装和运行webpack、gulp或vite等开发构建工具,实现: A、安装、导入和导出模块; B、合并、转译、压缩和打包模块 1、导出与导入 (1)node模块块开发:导出模块module.exports = {},导入模块require('./url/moduleName') (2)ES6模块块开发:导出模块export default xxx,导入模块import xxx from './url/moduleName' 2、__dirname和join var path = require("path"); console.log(__dirname); c:\Users\公司\Desktop\yuanma console.log(path.join(__dirname, "..", "src")); c:\Users\公司\Desktop\src console.log(path.join(__dirname, "../", "src")); c:\Users\公司\Desktop\src 3、__dirname和resolve var path = require("path"); console.log(__dirname); c:\Users\公司\Desktop\yuanma console.log(path.resolve(__dirname, "..", "src")); c:\Users\公司\Desktop\src console.log(path.resolve(__dirname, "../", "src")); c:\Users\公司\Desktop\src 4、url解析,问号传参给服务器,井号传参给浏览器 var url_ = 'http://www.zhu.cn:80/ccc/index.html?name=zxt&age=26#33'; var url = require('url'); console.log(url.parse(url_, true)); /* winUrl{ protocol: 'http:', slashes: true, auth: null, host: 'www.zhu.cn:80', port: '80', hostname: 'www.zhu.cn', hash: '#33', search: '?name=zxt&age=26', query: { name: 'zxt', age: '26' },//当true不存在时query: 'name=zxt&age=26', pathname: '/ccc/index.html', path: '/ccc/index.html?name=zxt&age=26', href: 'http://www.zhu.cn:80/index.html?name=zxt&age=26#33' } */ function queryUrlParam1(url) { var obj = {}; var reg1 = /([^?=&#]+)=([^?=&#]+)/g; var reg2 = /#([^?=&#]+)/; url.replace(reg1, function (keyValue,key,value) { obj[key] = value; }); if (reg2.test(url)) { obj['HASH'] = reg2.exec(url)[1]; } return obj; } function queryUrlParam2(url) { var obj = {}; var askText = ''; var hashText = ''; var askIndex = url.indexOf('?') === -1 ? url.length : url.indexOf('?'); var hashIndex = url.indexOf('#') === -1 ? url.length : url.indexOf('#'); if (askIndex < hashIndex) { askText = url.substring(askIndex + 1, hashIndex); hashText = url.substring(hashIndex + 1); } else { askText = url.substring(askIndex + 1); hashText = url.substring(hashIndex + 1, askIndex); } if (askText) { askText.split('&').forEach(function(item){ let [key, value] = item.split('='); obj[key] = value; }); } if (hashText) obj['HASH'] = hashText; return obj; }; console.log(queryUrlParam1("https://www.baidu.com/newspage/data/landingsuper?AAA=1111&BBB=222&CCC=333#1234")); console.log(queryUrlParam2("https://www.baidu.com/newspage/data/landingsuper?AAA=1111&BBB=222&CCC=333#1234")); 十一、npm(Node Package Manager,node包管理器,与node捆绑安装) 1、淘宝npm镜像使用方法 来源https://blog.csdn.net/quuqu/article/details/64121812 (1)临时使用 npm --registry https://registry.npm.taobao.org install express 验证 npm info express (2)持久使用 npm config set registry https://registry.npm.taobao.org 验证 npm config get registry (3)通过cnpm使用 npm install -g cnpm --registry=https://registry.npm.taobao.org 使用 cnpm install express 2、node包管理器之npm|cnpm|yarn|pnpm 注、node管理器之nvm (1)npm:最常用的包管理器 A、2010年发布 B、生成package-lock.json文件,从npm5.0(内置于2017年发布的node8.0.0)开始 C、按顺序依次下载每个包,速度慢 (2)cnpm:china npm,中国npm。 A、2014年发布,淘宝推出的npm替代工具 A、不生成lock文件 (3)yarn:纱线、故事 A、2016年发布 B、生成yarn.lock文件 C、并行下载包,速度块 D、2022年淘宝发布yarn的国内镜像tyarn (4)pnpm:performant npm,高性能的npm。 A、2016年11月发布,解决npm和yarn的扁平化安装方式带来的重复和幽灵依赖的问题 B、生成pnpm-lock.yaml文件 C、优势 a、将不同版本间有差异的文件添加到仓库,不会因为一个文件的改变,而复制整个新版本包的内容 b、所有文件都会存储在硬盘上的某一位置,允许跨项目地共享同一版本的依赖 D、使用 a、安装,npm install -g pnpm b、配置镜像源,pnpm config set registry https://registry.npm.taobao.org/ c、安装依赖,pnpm install 3、npm install 安装命令示例,包名和后面的参数,位置可以互换 (1)npm install 把package.json中的(开发devDependencies|开发生产dependencies)依赖配置,下载到本地项目的node_modules (2)npm install X 会把X包安装到项目的node_modules目录中,不会将模块依赖写入package.json中 (3)npm install X -g 会把X包安装到全局的node_modules目录中,不会将模块依赖写入package.json中 (4)npm install X --save 会把X包安装到项目的node_modules目录中,会在package.json的dependencies属性下添加X,--save简写为-S (5)npm install X --save-dev 会把X包安装到项目的node_modules目录中,会在package.json的devDependencies属性下添加X,--save-dev简写为-D 4、npm其它命令 (1)npm help <command>可查看某条命令的详情 (2)npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本 (3)npm run start/stop/test可以简写为npm start/stop/test (4)获取npm的安装路径,npm config get prefix 5、包的运行 (1)npm,必须在根目录下运行包,必须写入到package.json里面,且已安装 (2)npx,可以在任何地方运行包,不必写入到package.json里面,不必安装,从本目录开始逐层向上直至全局(nodejs环境变量)寻找该包, 找到就运行,没有找到就临时安装并直接运行,运行结束后删掉; 如果想运行层级以外的版本,则加上版本号即可,如npx webpack-dev-server@3.10.3; 6、“npm run dev”的运行说明 { "scripts": { "dev": "xxx abc", "serve": "vue-cli-service serve", }, } 注意:直接执行“xxx abc”命令会报错,因为操作系统里只有npm相关的命令 (1)在运行“npm install xxx”时,会在“node_modules/.bin”目录下,为包“xxx”创建名为“xxx”的3个文件, 没有后缀名的是对应Unix系的shell脚本,.cmd为后缀名的是windows bat脚本,.ps1为后缀名的则是PowerShell脚本(可以跨平台), 三者都是用node执行1个js文件 (2)“npm run dev”运行时,npm首先在"scripts"字段里,找到“dev”对应的“xxx”字段,然后在“node_modules/.bin”目录下, 找到名为“xxx”的文件,该文件从“node_modules/xxx”文件里引入相应文件,并运行"xxx abc" (3)运行后,缺包(出错、报错、错误)排查 A、node版本是否太新或太旧 B、本公司的公共包是否下载到对应位置 附、Live Server,一个快速启动的本地服务器 (1)在vscode的扩展中找到并安装 (2)在项目中-使用步骤, A、右键根目录下index.html; B、点击Open with Live Server C、改动该项目任意文件的内容,保存,自动刷新 (3)在demo中-使用步骤, A、右键任意.html文件; B、点击Open with Live Server C、改动该文件的内容,保存,自动刷新(无需点击浏览器的圆箭头) (4)主要功能 A、实时重新加载,自动检测文件变化,并立即在浏览器中刷新页面 B、跨域代理(需配置) 十二、gulp(JS打包工具,构建工具,自动化工具) 附、类似产品 (1)Webpack,2013年首发的前端构建工具 (2)Gulp,2015年首发的前端构建工具 (3)vite,2019年首发的前端构建工具 1、gulp的作用,借助于node环境,进行本地开发和打包生产 2、gulp的API (1)gulp.src(globs[, options])导入文件,参数为路径。webpack为entry自动导入文件 ----globs参数匹配文件路径(路径包括文件名,分为具体路径和通配符路径)。当有多个路径时,该参数为一个数组。 ----options为可选参数。通常情况下我们不需要用到。 ----* 匹配文件路径中的0个或多个字符,但不会匹配路径分隔符,除非路径分隔符出现在末尾 ----** 匹配路径中的0个到多个目录及其子目录,需要单独出现,即它左右不能有其他东西了。如果出现在末尾,也能匹配文件。 ----gulp.src(['js/*.js','css/*.css','*.html']) ----使用数组的方式还有一个好处就是可以很方便的使用排除模式,在数组中的单个匹配模式前加上!即是排除模式, 它会在匹配的结果中排除这个匹配,要注意一点的是不能在数组中的第一个元素中使用排除模式 ----gulp.src([*.js,'!b*.js']) //匹配所有js文件,但排除掉以b开头的js文件 ----gulp.src(['!b*.js',*.js]) //不会排除任何文件,因为排除模式不能出现在数组的第一个元素中 (2)gulp.dest(path[,options])导出文件的目录,参数为路径 ----path为写入文件的路径 ----options为一个可选的参数对象,通常我们不需要用到 ----gulp.dest()传入的路径参数,只能用来指定要生成的文件的目录,生成的文件名是由导入到它的文件流决定的。 ----gulp.dest(path)生成的文件路径是我们传入的path参数后面再加上gulp.src()中有通配符开始出现的那部分路径。例如: ----gulp.src('script/**/*.js') .pipe(gulp.dest('dist')); //最后生成的文件路径为 dist/**/*.js (3)gulp.task(name[, deps], fn)定义任务,参数为任务名 ----name 为任务名 ----deps 是当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。 ----fn 为任务函数,我们把任务要执行的代码都写在里面。 ----gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { //定义一个有依赖的任务 //Do something}); //gulp中执行多个任务,可以通过任务依赖来实现。例如我想要执行one,two,three这三个任务,那我们就可以定义一个空的任务,然后把那三个任务当做这个空的任务的依赖就行了,只要执行default任务,就相当于把one,two,three这三个任务执行了 ----gulp.task('default',['one','two','three']); ----如果任务相互之间没有依赖,任务会按你书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。 ----但是如果某个任务所依赖的任务是异步的,就要注意了,gulp并不会等待那个所依赖的异步任务完成,而是会接着执行后续的任务。 (4)gulp.watch(globs[, opts], tasks)或gulp.watch(glob[, opts], cb)用来监视文件的变化,参数为路径,实际项目中,用Gaze插件代替 ----globs参数匹配文件路径(路径包括文件名,分为具体路径和通配符路径)。当有多个路径时,该参数为一个数组。 ----opts 为一个可选的配置对象,传给gaze的参数,通常不需要用到 ----tasks 为文件变化后要执行的任务,为一个数组 ----cb参数为一个函数。每当监视的文件发生变化时,就会调用这个函数,并且会给它传入一个对象,该对象包含了文件变化的一些信息,type属性为变化的类型,可以是added,changed,deleted;path属性为发生变化的文件的路径 ----gulp.watch('js/**/*.js', function(event){ console.log(event.type); //变化类型 added为新增,deleted为删除,changed为改变 console.log(event.path); //变化的文件的路径 }); 3、gulp的插件 (1)gulp-load-plugins,加载其它插件 (2)gulp-inject,把文件插入到注释的位置 (3)gulp-useref,把HTML引用的多个文件如CSS、JS合并起来,再用gulp-if进行分类 (4)gulp-clean,把原来的文件清空 (5)gulp-if,判断文件类型 (6)gulp-htmlmin,压缩HTML代码 (7)gulp-clean-css,压缩CSS代码 (8)gulp-uglify,压缩JS代码 (9)gulp-ng-annotate,解决angular中,依赖注入出错的问题 (10)gulp-angular-templatecache,html压缩成js后,用此插件指定该js所属模块 (11)gulp-rev,为静态文件随机添加一串hash值,同时生成manifest.json保存新旧文件名对应关系 (12)gulp-rev-collector,根据gulp-rev生成的manifest.json文件中的映射,去替换文件名称 4、gulp应用实例 (1)全局内容 var Gaze = require('gaze').Gaze; var gaze = null; var gaze1 = null; function getMenus(exclude){ var last_menus = []; var all_menus = eval( fs.readFileSync('src/config/project/cy/menu.js', 'utf8'); ); //menu.js自执行函数,返回一个数组 if(exclude){ var firstGrade = Object.keys(exclude); var length1 = all_menus.length; for(var i=0;i<length1;i++){ var sub1 = deepCopy(all_menus[i]); if(firstGrade.indexOf(sub1.title) === -1){ var subs2 = sub1.subs; var length2 = sub1.subs.length; var secondGrade = Object.keys(exclude[sub1.title]); for(var j=0; j<length2; j++){ if(secondGrade.indexOf(subs2[j].title) > -1){ subs2.splice(j,1); j--; }else{ var subs3 = subs2.subs; var length3 = subs2.subs.length; var thirdGrade = Object.keys(exclude[sub1.title][subs2[j].title]); for(var jj=0; jj<length2; jj++){ if(thirdGrade.indexOf(subs3[jj].title) > -1){ subs3.splice(jj,1); jj--; } } } } last_menus.push(sub1) } } }else{ last_menus = deepCopy(all_menus); } //另外,计算图标位置的函数可以写在全局,在last_menus内调用 var allData = `(function () { var base_dir = 'cy'; var menus = last_menus; angular .module('app') .constant('menus', menus); })();` fs.writeFile( 'src/config/menus.js', allData, 'utf8', function () {} ); } (2)本地开发任务 function testServeConfig(configDirname) {//实际上,下面(1)(2)两种情况是特殊的http请求,剩下的http请求都是直接向本机发送url var array = ['/app','/oauth','/status'];//(1)跨域代理:开发过程中,以这些item开始的url,将向后台服务器发送以item开始的url。这种情况主要用于获取动态数据。 var proxy_ = array.map(function (value) { var a = url.parse('https://172.18.10.23' + value); a.route = value; a.rejectUnauthorized = false; return proxy(a); }); browserSync.init( { port: 8900, notify: false, open: false, server: { baseDir: ['src'], directory: false, index: 'index.html', middleware: proxy_, routes: { '/audit-html/static/img': 'src/img',//(2)本地代理:开发过程中,以key开始的url,将向本地服务器发送以value开始的url。这种情况主要用于获取静态资源,如html文件、css的背景图片和img标签的src属性。 } } }, function () { reload(); //渲染index.html //执行main.js,根据menus服务,执行$stateProvider.state(it.state, it.cfg);关联状态、页面、控制器 } ); } function testServe(moduleDirName, configDirname) { fs.exists('devServerConfig.json', function (exists) { if (gaze && gaze1) { gaze.close(); gaze1.close(); browserSync.exit(); } inject_file(moduleDirName, configDirname);//注入文件,(不应该通过menu.js)将各文件夹下的js和css文件注入到index.html, watch_file(moduleDirName, configDirname);//监听文件,监听各文件夹下的js和css文件 if (exists) { testServeConfig(configDirname);//配置服务 } else { fs.writeFile( 'devServerConfig.json', '{"address":"http://192.168.80.152:7300"}', 'utf8', function () { testServeConfig(configDirname);//配置服务 } ); } }); } gulp.task('default', function () { var exclude = { '一级标题1':{ '二级标题1':{ '三级标题1':{ '四级标题1':'' } }, } } getMenus(exclude);//把参数包含的标题,从总数据中排除,获取最终的menus服务 testServe('cy','cy');//注入文件、监听文件、配置服务 }); (3)打包生产任务 gulp.task('templates', function () { gulp .src(tpl_html) .pipe( $.htmlmin({ collapseWhitespace: true, removeComments: true, minifyCSS: true }) ) .pipe( $.angularTemplatecache({ /* html压缩成js后,用此插件指定该js所属模块 */ module: 'app' }) ) .pipe($.uglify()) .pipe(gulp.dest('.tmp')); }); gulp.task('buildAll', function () { gulp .src(['src/index.html']) .pipe( $.inject(gulp.src('.tmp/templates.js'), { starttag: '<!-- inject:partials -->', relative: true }) ) .pipe($.useref()) .pipe($.if('*.js', $.ngAnnotate())) /* 解决angular中,依赖注入出错的问题 */ .pipe($.if('*.js',$.uglify())) /* 压缩JS代码 */ .pipe($.if( '*.css', autoprefixer({ browsers: ['last 8 versions'], cascade: false }) )) .pipe($.if('*.css', $.cleanCss()))/* 压缩CSS代码 */ .pipe(gulp.dest('dist')); }); gulp.task('revCssJS', function () { gulp .src(['./dist/style/*.css', './dist/script/*.js']) .pipe(rev()) //添加hash后缀 .pipe(gulp.dest('./dist')) //原路导出“添加hash后缀”后的文件 .pipe(rev.manifest()) //生成映射文件 .pipe(gulp.dest('./rev')); //将映射文件导出到rev }); gulp.task('revHtml', function () { gulp .src(['./rev/*.json', './src/productTpl/index.html']) .pipe(revCollector()) //用前者的映射关系替换后者相应的文件 .pipe(gulp.dest('./template')); //将替换后的文件导出 }); gulp.task('build', function () { runSequence( 'clean:start', 'template', 'images-cy', 'copy', 'buildAll', //产生all.min.js 'revCssJS', 'revHtml', 'clean:final'/* , function () { copyConfiguration('cy'); } */ ); }); 十三、webpack(JS打包工具,构建工具,自动化工具) 附、类似产品 (1)Webpack,2013年首发的前端构建工具 (2)Gulp,2015年首发的前端构建工具 (3)vite,2019年首发的前端构建工具 1、webpack各版本发布时间 (1)Webpack1,2013 (2)Webpack2,2016,支持ES Module,支持Tree-Shaking,将没用到的代码剔除 (3)Webpack3,2017.06.20,Scope Hoisting(作用域提升)和Magic Comment(魔法注释)等 (4)Webpack4,2018.02.25,mode属性,WebAssembly(字节码格式),支持多种模块类型,0配置等 (5)Webpack5,2020.10.10,优化缓存,改善Tree-Shaking等等 2、常见包 (1)webpack:是打包的命令,也是模块打包器。将entry输入的文件,经过链式裂变导入、用module.rules编译打包后, 根据mode取值不同而选择压不压缩最终的js,最后通过output把js显性输出到指定目录。 (2)webpack-cli:是webpack命令行的工具,能使webpack命令带参数在命令行中运行,cli即命令行接口(Command Line Interface)。 (3)webpack-dev-server:是开启本地node服务器的命令,也是一个小型的node服务器。将entry输入的文件用module.rules编译打包后, 根据mode取值不同而选择压不压缩最终的js,最后通过output把js隐性输出到内存并自动打开浏览器,同时监听entry及其import引入的文件, 一旦发生变化就自动编译、打包、隐性输出代码,手动刷新浏览器,可以看到最新效果。 3、常见疑问释疑 (1)直接运行的包,如果本目录或全局安装了该包,可以直接运行,不需要npm和npx,如webpack、webpack-dev-server。 (2)在编译的过程中,根据babel-loader的配置处理js的兼容,根据process.env.NODE_ENV取值不同,选择package.json里browserslist的不同配 置项来处理css的兼容问题,根据插件配置决定最终的css压不压缩和输出目录,根据url-loader的配置决定最终的img输出目录和公共路径。 4、webpack优化方法 (1)数组一个,只执行数组选项中的一个,oneOf:[] (2)一个数组,将多次使用的加载器放到一个数组里,供展开使用 (3)懒加载,当需要使用文件时才加载,比如点击某个按钮后才加载某个文件 (4)多进程,见下面配置thread-loader (5)使用cache,缓存,当只有js/css文件发生改变时,只打包js/css文件到最终目录里,用最新的js/css文件名替换掉index.html上原来的 文件名,同时删掉原来的js/css文件 (6)使用HMR,模块热替换,在程序运行过程中替换、添加或删除“模块”,而无需重新“加载整个页面”,Hot Module Replacement的缩写 自悟,单页面应用,每个页面都由多个模块构成,哪个模块更新,就替换掉哪个模块,不会重新构建所有模块,最后重新渲染当前页面 (7)使用dll,动态链接库,把第三方类库单独打包,生成映射库,然后每次只打包项目自身代码,Dynamic Link Library的缩写, (8)使用externals,排除第三方类库,用CND引入第三方库 (9)使用gzip压缩,Webpack用compression-webpack-plugin对静态资源进行压缩,上传至服务器;服务器端根据请求头返回gzip资源, 浏览器根据响应头解压gzip资源 来源,https://blog.csdn.net/weixin_47516343/article/details/125392505 (10)弃用sourcemap,资源地图,module.exports = { productionSourceMap: false, }//打包时不会生成 .map 文件,加快打包速度 (11)使用webpack-bundle-analyzer,webpack打包分析,对webpack的打包性能进行分析 (12)使用webpack的speed-measure-webpack-plugin看打包速度 附1、cache示例 { test: /\.js$/, use: ['cache-loader', 'babel-loader'], }, { filename: "js/built.[contenthash:10].js" } { filename: "css/built.[contenthash:10].css" } 附2、HMR示例 const webpack = require('webpack'); module.exports = { //... plugins: [ new webpack.HotModuleReplacementPlugin() ], devServer: { hot: true } } 附3、dll示例 来源,https://www.jb51.net/article/118431.htm A、把第三方类库单独打包,如:jquery、react、vue... B、生成映射库,webpack.dll.config.js文件说明 module.exports = { entry: { //导入 vendors: ['react', 'lodash'] //别名:["库名","库名"] }, output: { //导出 filename: '[name].js', //生产环境需要导入该文件 path: resolve(__dirname, 'dll'), library: '[name]_[hash]' //打包库里向外文件的名称 }, plugins: [ new webpack.DllPlugin({ //生成映射库 path: resolve(__dirname, "dll/manifest_[name].json"), name: "[name]_[hash]", //打包库里向外文件的名称,与output.library必须一致 }), ] }; C、自动生成动态链接库,webpack.config.js文件说明,注意 (1)路径中不能有../, (2)同级paceage.json中,要安装dll-link-webpack-plugin, (3)全局node、webpack、webpack-cli的版本 var DllLinkPlugin = require("dll-link-webpack-plugin"); module.exports = { plugins: [ new DllLinkPlugin({ config: require("./webpack.dll.config") }) ] }; 5、常见术语释义 (1)chunk,代码块 (2)polyfill,兜底,在计算机中,变相实现不能直接实现的操作 (3)bootstrap,n.[计]引导程序,辅助程序;vt.启动(电脑) (4)process,对象是一个 global 变量,提供有关当前 Node.js 进程的信息并对其进行控制 (5)dll,动态链接库英文为DLL,是Dynamic Link Library的缩写。(Dynamic,[daɪˈnæmɪk],有活力的) 6、package.json (1)webpack开发/打包命令,npm run dev/build { "scripts": { "dev": "cross-env envNum=0 webpack-dev-server --config build/webpack.dev.conf.js", //用webpack-dev-server开启代理服务器,process.env.envNum的值为0 "build": "cross-env envNum=1 webpack build/webpack.dev.conf.js"//用webpack打包 //cross-env,在windows环境下可改为set,在其它环境下可省略 //envNum的值,可以用于区分开发、生产环境 }, "dependencies": {}, "devDependencies": { "babel-core": "6.26.0",//把ES6+换为ES5的核心插件 "babel-preset-es2015": "^6.24.1",//2017年Babel宣布ES2015/ES2016/ES2017被废弃 "babel-preset-env": "^1.7.0",//根据配置的目标浏览器或运行环境,自动将ES2015+转换为ES5。 "babel-loader": "7.1.5",//将ES2015+转换为ES5。 "babel-eslint": "8.2.6",//ES6+语法检测。 "url-loader": "1.0.1",//将引⼊的图⽚以base64编码并打包到⽂件中,最终只需要引⼊这个dataURL就能访问图⽚了。 "vue-loader": "15.3.0",//解析和转换.vue文件,提取出其中的逻辑代码script、样式代码style、以及HTML模版template,再分别把它们交给对应的 Loader 去处理。 "vue-style-loader": "4.1.2",//除了支持客户端渲染,还支持服务端渲染 } } (2)webpack-dev-server命令下,各参数的含义 config,修改默认的配置文件 content-base,与path、publicPath类似 compress,开启gzip压缩 hot,不刷新浏览器 inline,刷新浏览器 hot --inline,失败则刷新浏览器 progress,显示打包的进度 quiet,控制台中不输出打包的信息 HRM:Hot-Module-Replacement,模块热更新。在应用运行时,无需刷新页面,便能替换、增加、删除必要的模块 7、webpack.config.js文件示例--1 注意:各项配置中,mode、plugins、publicPath先执行,module、entry、output后执行。非常重要!!! const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const webpack = require('webpack'); module.exports = { entry: './src/js/index.js',//引入js,字符串和数组为单入口,对象为多入口;另外对象的value也可以是数组 output: { filename: 'js/built.js',//导出js文件,如果和entry路径的深度不一样,那么webpack会自动调整图片的引用路径 path: resolve(__dirname, 'build')//里面的css默认或者通过配置导出 }, publicPath: '/',//给所有资源引入公共路径前缀'/',如'imgs/a.jpg'==>'/imgs/a.jpg' mode: 'development',//用webpack打包时不压缩,取'production'则压缩 module: { rules: [//loader配置 { test: /\.less$/,//处理less资源 use: ['style-loader', 'css-loader', 'less-loader']//从右向左执行 //'style-loader'开发环境用。在下面的js文件里,隐式地为每个样式文件创建一个style标签并将该样式放入,再将每一个style标签插到head标签里;隐式地自动将output.filename引给JavaScript标签并插入到template里的body标签里。这样head标签里不会有style标签,但控制台里有。 //'css-loader',将css文件整合到js文件中 //'less-loader'将less文件转换为css文件 }, { test: /\.css$/, use: [//从下向上执行 MiniCssExtractPlugin.loader,//把下面js文件里的样式提取到单独的样式文件里,根据new MiniCssExtractPlugin确定文件名,然后插到由html-webpack-plugin指定的html下的head下的link标签里 'css-loader',//将css文件整合到js文件中 { loader: 'postcss-loader',//css兼容处理 /* 一、css兼容处理: 1、postcss-loader(提供postcss) 2、postcss-preset-env(帮postcss找到package.json中browserslist里面的配置,加载指定的css兼容性样式) 3、package.json中的相关配置 "browserslist": { //浏览器列表 "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ], "production": [//默认是看生产环境 ">0.2%", "not dead", "not op_mini all" ] } */ options: { ident: 'postcss', plugins: () => [//postcss的插件 require('postcss-preset-env')() ] } } ] }, { test: /\.js$/,//工程化优化之使用缓存 use: ['cache-loader', 'babel-loader'], }, { test: /\.js$/, exclude: /node_modules/, use: [ { /* 开启多进程打包。 进程启动大概为600ms,进程通信也有开销。 只有工作消耗时间比较长,才需要多进程打包 */ loader: 'thread-loader', options: { workers: 2 //进程2个 } }, { loader: 'babel-loader',//js兼容处理 /* 二、js兼容处理: 1、基本兼容处理如箭头函数,用(1)babel-loader(显性使用)(2)@babel/core(隐性支持)(3)@babel/preset-env(显性使用) 2、复杂兼容处理如promise,除了1以外,还需要用按需加载的(4)core-js(显性使用)或全部引入的(5)@babel/polyfill(显性使用) 3、【babel-loader@8 requires Babel 7.x (the package '@babel/core')】 */ options: { presets: [//预设:指示babel做怎么样的兼容性处理 [ '@babel/preset-env', { useBuiltIns: 'usage',//按需加载 corejs: {version: 3},//指定core-js版本 targets: {//指定兼容性做到哪个版本浏览器 chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } } ] ], cacheDirectory: true//开启babel缓存,第二次构建时,会读取之前的缓存 } } ] }, { test: /\.js$/, exclude: /node_modules/, include: resolve(__dirname, 'src'),//只检查 src 下的js文件 enforce: 'pre',//2个js匹配,这个优先执行。也可以像处理css文件那样,把2个包放到1个use数组里。延后执行'post' loader: 'eslint-loader', /* 三、js语法检查: 1、eslint-loader(显性使用) eslint(隐性支持),eslint-config-airbnb-base(隐性支持) eslint-plugin-import(隐性支持) 2、package.json中的相关配置 "eslintConfig": { "extends": "airbnb-base" } 3、//eslint-disable-next-line,下一行所有eslint规则都失效 */ options: { fix: true//自动修复eslint的错误 } }, { test: /\.(jpg|png|gif)$/,//处理.css中的图片 loader: 'url-loader',//在.css中的图片,通过css-loader存储到js文件中,在js文件里改名后,可根据图片大小将图片转码为base64保存在js中或用common.js(默认用ES6,在配置中被esModule:false关闭)模块导出。js里存储了图片改前名和改后名的关联,后来再用到这张图片时会用到这个关联。 options: { limit: 8 * 1024, name: '[hash:10].[ext]', esModule: false,//关闭es6模块化 outputPath: "img", publicPath: "/img", } }, { test: /\.html$/,//处理html中的图片 loader: 'html-loader'//在.html文件img标签中的图片,用common.js模块处理,从而能被url-loader进行处理。 }, { exclude: /\.(html|js|css|less|jpg|png|gif)/,//处理其他资源 loader: 'file-loader',//原封不动地输出文件 options: { name: '[hash:10].[ext]', outputPath: 'media', publicPath: "/media", }, } ] }, plugins: [ //1、以下指定CSS的导出位置的导出后的名字 new MiniCssExtractPlugin({ filename: 'css/built.css'//导出css文件。默认配置为filename: './main.css',最终的js按照output导出 }), new OptimizeCssAssetsWebpackPlugin(),//对上面导出的css进行压缩 //2、以下生成js文件 new DllLinkPlugin({ //生成动态链接库存并放到"dll/manifest_[name].json"里 config: require('webpack.dll.config.js') }), new webpack.DllReferencePlugin({//使用动态链接库,把依赖的名称映射到模块的id上 manifest: resolve(__dirname, "dll/manifest_[name].json"), }), new AddAssetHtmlWebpackPlugin([//添加资源库,将资源打包出去,并在html中自动引入 { filepath: resolve(__dirname, "dll/jquery.js") },//有多项时,用数组 { filepath: resolve(__dirname, "dll/vue.js") },//只有一项时,直接用对象即可 ]), //3、以下将导出的js文件或css文件插入到html文件head的link里或body的script里 new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true,//去除index.html里面的空白 removeComments: true//去除index.html里面的注释 } }), ], devServer: {//开发环境必有 compress: true,//启动gzip压缩 contentBase: resolve(__dirname, 'build'),//告诉服务器内容的来源。仅在需要提供静态文件时才进行配置。 watchContentBase: true,//监视 contentBase 目录下的所有文件,一旦文件变化就会 reload watchOptions: { ignored: /node_modules///忽略文件 }, port: 5000,//端口号 host: 'localhost', open: true,//自动打开浏览器 hot: true,//开启HMR功能,需要插件webpack.HotModuleReplacementPlugin配合 proxy: {//服务器代理 --> 解决开发环境跨域问题 '/api': {//一旦服务器5000接收到'/api/xxx'的请求 target: 'http://localhost:3000',//就会把请求转发到另外一个服务器3000 pathRewrite: {//发送请求时,请求路径重写:将/api去掉 '^/api': '' } } }, clientLogLevel: 'none',//不要显示启动服务器日志信息 quiet: true,//除了一些基本启动信息以外,其他内容都不要显示 overlay: false,//如果出错了,不要全屏提示~ }, devtool: "nosources-source-map",//正常情况下,此配置项缺失比加上更好 resolve: {//解析模块的规则 alias: {//配置解析模块路径别名: 优点简写路径 缺点路径没有提示 $css: resolve(__dirname, 'src/css') }, extensions: ['.js', '.json', '.jsx', '.css'],//配置省略文件路径的后缀名 modules: [resolve(__dirname, '../../node_modules'), 'node_modules']//告诉 webpack 解析模块是去找哪个目录 } optimization: { /* 可以将node_modules中(除dll外)代码单独打包一个chunk最终输出 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk */ splitChunks: { chunks: 'all' } }, externals: {//用CDN时,需要此项配置;用dll时不能有此项配置。 jquery: "jquery", //不打包第三方类库jquery vue: "vue", //不打包第三方类库vue }, }; 8、webpack.config.js文件示例--2 // 附、环境类型、NODE_ENV的设置与获取 // a、设置,package.json // { // "scripts": { // //"dev": "webpack --mode development"//不适合持续的开发调试 // //"dev": "NODE_ENV=development webpack-dev-server"//适合持续的开发调试,是一个相对成熟的独立开发服务器工具 // //"dev": "NODE_ENV=development webpack serve"//适合持续的开发调试,Webpack新的开发服务器命令,旨在逐步替代上面 // } // } // b、获取,webpack.config.js // process.env.NODE_ENV = "development"; //不能这样设置!!! // module.exports = { // mode: process.env.NODE_ENV,//获取、设置 // }; // 附、环境变量、环境对象的设置与获取 // a、设置,package.json // { // "scripts": { // //"dev": "cross-env envNum=0 webpack-dev-server",//设置 // } // } // b、获取,webpack.config.js // var envNum = process.env.envNum; var webpack = require('webpack'); var path = require('path'); var envNum = process.env.envNum;//0为开发环境,1为生产环境,package.json文件里有定义 var proxyServer = "http://192.168.10.231:8081/" var baseRules = getBaseRules(); var cssRules = getCssRules({ extract: [false,true][envNum], sourceMap: [false,true][envNum], usePostCSS: [false,true][envNum], }) module.exports = { mode: ['development','production'][envNum] , context: path.resolve(__dirname, '../'), entry: { app: './src/main.js',//app与其中一个chunks的最后一项对应 report: './src/main-report.js'//report与其中一个chunks的最后一项对应 }, output: { filename: '[name].js', path: config.build.assetsRoot,//打包后的存放位置(绝对) publicPath: ['/','/dist/'][envNum]//打包时静态资源的存放位置(相对与path) }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src') } }, module: { rules: baseRules.concat(cssRules) }, devtool: ["cheap-source-map","source-map"][envNum], devServer: { host: 'localhost',//默认是localhost。如果你希望服务器外部可访问,指定如 host: '0.0.0.0' port: '7770', proxy: { "/app": { target: proxyServer, //目标接口域名 changeOrigin: true, //是否跨域 pathRewrite: { "^/app": "/" //重写接口 } }, "/isgapp": { target: proxyServer, //目标接口域名 changeOrigin: false, //是否跨域 pathRewrite: { "^/isgapp": "/isgapp" //重写接口 } }, "/dist": { target: `http://${devConfig.host}:${devConfig.port}/`, //目标接口域名 changeOrigin: false, //是否跨域 pathRewrite: { "^/dist/static/data": "/src/project/"+process.env.configDir, //重写接口 "^/dist": "/", //重写接口 } }, }, }, plugins: [[ new webpack.DefinePlugin({ 'process.env': merge({ configDir: JSON.stringify(process.env.configDir) },require('../config/dev.env')) }), new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true, favicon: resolve('favicon.ico'), chunks: ['manifest', 'vendor', 'app'] }), new HtmlWebpackPlugin({ filename: 'report-index.html', template: 'report-index.html', title: '报表管理', favicon: resolve('favicon.ico'), inject: true, chunks: ['manifest', 'vendor', 'report'] }), new VueLoaderPlugin() ],[ new webpack.DefinePlugin({ 'process.env': merge({ configDir: JSON.stringify(process.env.configDir) },env) }), new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash:8].css',//导出文件的文件名 chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')//导入文件的文件名 }), new HtmlWebpackPlugin({ filename: path.resolve(__dirname, "../dist/index.html"), template: 'index.html', inject: true, favicon: resolve('favicon.ico'), minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true }, chunks: ['manifest', 'vendor', 'app'] }), new HtmlWebpackPlugin({ filename: config.build.report, template: 'report-index.html', inject: true, favicon: resolve('favicon.ico'), minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true }, chunks: ['manifest', 'vendor', 'report'] }), new ScriptExtHtmlWebpackPlugin({ inline: /runtime\..*\.js$/ }), new webpack.NamedChunksPlugin(chunk => { if (chunk.name) { return chunk.name } const modules = Array.from(chunk.modulesIterable) if (modules.length > 1) { const hash = require('hash-sum') const joinedHash = hash(modules.map(m => m.id).join('_')) let len = nameLength while (seen.has(joinedHash.substr(0, len))) len++ seen.add(joinedHash.substr(0, len)) return `chunk-${joinedHash.substr(0, len)}` } else { return modules[0].id } }), new webpack.HashedModuleIdsPlugin(), new CopyWebpackPlugin([{ from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] }, { from: path.resolve(__dirname, '../src/project/'+process.env.configDir+'/setting.json'), to: config.build.assetsSubDirectory+'/data', }, ]), new VueLoaderPlugin() ]][envNum] }; function getBaseRules(){ return [ ...(config.dev.useEslint ? [createLintingRule()] : []), { test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, { test: /\.js$/, loader: 'babel-loader', include: [ resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client') ] }, { test: /\.svg$/, loader: 'svg-sprite-loader', include: [resolve('src/icons')], options: { symbolId: 'icon-[name]' } }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', exclude: [resolve('src/icons')], options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('media/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] } function getCssRules(options){ options = options || {}; var extract = options.extract; var sourceMap = options.sourceMap; var usePostCSS = options.usePostCSS; function getLoader(loader, other) { var loaders = []; if (extract) { loaders.push(MiniCssExtractPlugin.loader)//把下面js文件里的样式提取到单独的样式文件里,然后插在head标签下,由html-webpack-plugin自动生成的link标签里 } else { loaders.push('vue-style-loader')//创建了一个style标签,然后直接插入到了head标签中 }; loaders.push({ loader: 'css-loader',//将css文件整合到js文件中 options: { sourceMap: sourceMap } }); if (usePostCSS) { loaders.push({ loader: 'postcss-loader', options: { sourceMap: sourceMap } }) }; if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, other, { sourceMap: sourceMap }) }) }; return loaders }; return [ { test: /\.css$/, use: getLoader() }, { test: /\.less$/, use: getLoader('less') }, { test: /\.sass$/, use: getLoader('sass', { indentedSyntax: true }) }, { test: /\.sass$/, use: getLoader('sass') } ] } 十四、vite,一种新型前端构建工具(JS打包工具,构建工具,自动化工具) 附、类似产品 (1)Webpack,2013年首发的前端构建工具 (2)Gulp,2015年首发的前端构建工具 (3)vite,2019年首发的前端构建工具 1、webpack打包与困境 (1)webpack将模块文件打包成浏览器支持的文件 (2)构建大型应用时,即使使用模块热替换(HMR),webpack的反应也不够迅速 2、vite的组成 (1)开发服务器,它基于原生ES模块提供了丰富的内建功能 (2)构建指令,它用预(默认)配置和多种模块、静态分析的Rollup打包代码,输出用于生产环境的高度优化过的静态资源 3、vite的优势,Vite将应用中的模块分为依赖和源码两类 (1)预构建,用Go语言编写的esbuild“预构建-依赖模块”,比用JS编写的打包器“预构建-依赖模块”快10-100倍 A、预构建,先将作为CommonJS或UMD发布的依赖项转换为ESM模块,再将ESM多模块依赖转为单模块依赖 B、SFC,单文件组件,Single File Component (2)按需转译,“按需转译-ES组件模块” (3)热更新,“热更新HMR-ES组件模块” 4、vite的命令 附、全局安装vite,(c)npm install -g create-vite (1)Vite,启动开发服务器 (2)vite build,构建生产版本 (3)vite optimize,预构建依赖 (4)vite preview,本地预览构建产物 5、静态资源处理 (1)将资源引入为URL,import imgUrl from './img.png', 应当为,import zanIcon from '@/assets/images/zan.png'; imgUrl在开发时会是/img.png,在生产构建后会是/assets/img.2d8efhg.png (2)显式引入URL,使用?url后缀显式导入为一个URL,import workletURL from 'extra-scalloped-border/worklet.js?url' (3)将资源引入为字符串,import shaderString from './shader.glsl?raw' (4)导入脚本作为Worker,import Worker from './shader.js?worker' (5)public目录,静态资源默认放在<root>/public里,如public/icon.png,在源码中被引用为/icon.png, public中的资源不应该被JavaScript文件引用 6、构建生产版本并部署, (1)运行vite build命令, (2)使用<root>/index.html作为其构建入口点, (3)生成能够静态部署的应用程序包dist文件夹, (4)部署文件夹到服务器 7、vite.config.js示例 //附、参考文档,https://cn.vitejs.dev/guide/why.html //附、环境变量、环境对象的设置与获取 // (1)设置,.env.development,示例如下 // VITE_APP_BASE_API = 'http://10.51.29.56:7010/ai-access-server/' // (2)获取,request.js,示例如下 // const service = axios.create({ // baseURL: import.meta.env.VITE_APP_BASE_API, //axios中请求配置有baseURL选项,表示请求URL公共部分 // timeout: 180000, //超时 // }) //来源ai-web import { fileURLToPath, URL } from 'node:url' import path from 'path' import { defineConfig, loadEnv } from 'vite' //定义配置,加载环境 import vue from '@vitejs/plugin-vue' import { viteMockServe } from 'vite-plugin-mock' import createVitePlugins from './vite/plugins' import legacy from '@vitejs/plugin-legacy' //为传统浏览器提供支持 export default defineConfig((params)=>{ // console.log( params ); // { // mode: 'development', //环境类型、NODE_ENV的设置与获取(无需设置,用params.mode获取) // command: 'serve', // ssrBuild: false // } const envConfig = loadEnv(params.mode, process.cwd()); // 默认不加载.env文件,Vite的loadEnv函数可以加载指定的.env文件 // loadEnv(mode, envDir, prefixes)同步函数的参数说明 // 1、mode,根据启动命令,确定mode值,加载对应的.env文件。配置文件提供一切 // A、为development时,加载.env.development文件; // B、为production时,加载.env.production文件 // 附、启动命令有,npm run dev;npm run build // 2、envDir,当前工作目录, // A、相对于package.json的文件夹地址, // B、可通过process.cwd()获取 // C、console.log(process.cwd()); //C:\Users\Haier\Desktop\ai-web; // D、cwd,Current Working Directory,即当前工作目录 // 3、prefixes,根据prefixes值,返回文件里的项 // A、缺失时,返回对应文件中,以字符串`VITE_`开始的项; // B、为''时,返回对应文件中,所有项和内置项; // C、为字符串时,返回对应文件中,以该字符串开始的项 const { VITE_BASE_URL, VITE_OUTPUT_DIR, VITE_USE_MOCK } = envConfig return { base: VITE_BASE_URL,// 在开发环境或生产环境中,请求静态资源时,都会加上。比如/app/image.jpg中的/app // 来源,https://cn.vitejs.dev/config/shared-options.html#define // define,定义全局常量,在开发环境下定义在全局,在构建时被静态替换 // define,全局常量获取,console.log(__APP_ENV__); define: {//对于字符串以外的数据类型(如布尔值)最好使用JSON.stringify进行处理,以确保在不同环境下的正确替换 'process.env': envConfig, __APP_ENV__: envConfig.APP_ENV, __APP_VERSION__: JSON.stringify('v1.0.0'), __API_URL__: 'window.__backend_api_url', //__DEBUG_MODE__: JSON.stringify(true), }, // vite中define数据和环境变量有什么区别? // 1、define,在构建时,静态地注入到代码中 // 2、环境变量,在不同环境下,项目参数可能变化 // 3、环境变量的优先级高于define plugins: [ createVitePlugins(), viteMockServe({ // 问:在vite.config.js中,在plugins中引入mock.js,devServer.proxy还有效吗 // 答:1个请求用3种方案中的1种来处理,优先级从大往小为,devServer.proxy、Mock.js、直接发出(浏览器跨域) // 在“开发环境或生产环境”中,请求静态资源时,下面配置为true的,会被模拟 mockPath: "./src/mock/",//导入此路径下的-虚拟数据 prodEnabled: false,//生产环境,禁用-模拟数据-服务 localEnabled: VITE_USE_MOCK,//true,开发环境,启用-本地模拟数据-服务,network有相关记录 }), ], build: { outDir: VITE_OUTPUT_DIR }, css: { preprocessorOptions: { scss: { additionalData: `@use "@/assets/css/variables.scss" as *;`, }, }, }, resolve: { alias: { '~': path.resolve(__dirname, './'), '@': path.resolve(__dirname, './src') }, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] }, server: { port: 80, host: true, open: true, proxy: { // 原理:改变源(不是跨域) // (1)在开发环境中,请求从浏览器发到开发服务器,在开发服务器改变源后,再发到后台服务器 // (2)在生产环境中,请求从浏览器发到后台服务器,不可以改变源 // 仅在“开发环境”中,请求动态资源时,下面配中的,会被代理 // https://cn.vitejs.dev/config/#server-proxy '/sdapi':{ target: 'http://192.168.116.32:7860', changeOrigin: true, }, '/play':{ target: 'http://192.168.116.32:8002', changeOrigin: true, }, '/create_task':{ target: 'http://192.168.116.32:8002', changeOrigin: true, }, '/task_status':{ target: 'http://192.168.116.32:8002', changeOrigin: true, }, '/download':{ target: 'http://192.168.116.32:8002', changeOrigin: true, }, '/api': { target: 'https://reg.cctv.com', changeOrigin: true, rewrite: (p) => p.replace(/^\/api/, '') } } }, } }) 8、完整案例之package.json { "name": "online-class-web", "version": "0.0.1", "description": "在线课堂", "author": "maxiaolin", "license": "MIT", "scripts": { "dev": "vite", //启动开发服务器 "build:prod": "vite build", //打包 "build:stage": "vite build --mode staging", "preview": "vite preview" //预览 }, "type": "module",//此项配置,并非来自真实项目 "repository": { "type": "git", "url": "http://10.71.59.14:30190/media/online-class-web.git" }, "dependencies": { "@element-plus/hooks": "^0.0.5", "@element-plus/icons-vue": "^2.0.10", "@element-plus/utils": "^0.0.5", "@vueuse/core": "9.5.0", "axios": "0.27.2", "element-plus": "2.2.27", "file-saver": "2.0.5", "fuse.js": "6.6.2", "js-cookie": "3.0.1", "jsencrypt": "3.3.1", "mockjs": "^1.1.0", "nprogress": "0.2.0", "pinia": "2.0.22", "video.js": "^7.20.3", "videojs-contrib-hls": "^5.15.0", "vite-plugin-mock": "^3.0.0", "vue": "3.2.45", "vue-cropper": "1.0.3", "vue-router": "4.1.4" }, "devDependencies": { "@vitejs/plugin-vue": "3.1.0", "@vue/compiler-sfc": "3.2.45", "sass": "1.56.1", "unplugin-auto-import": "0.11.4", "vite": "3.2.3", "vite-plugin-compression": "0.5.1", "vite-plugin-svg-icons": "^2.0.1", "vite-plugin-vue-setup-extend": "0.4.0" } } 十五、Rollup,JS模块打包工具 1、Rollup的功能 (1)Tree-Shaking,将没用到的代码剔除 (2)兼容CommonJS模块 (3)兼容ES模块 2、配置文件 (1)配置文件示例,rollup.config.js import json from“ rollup - plugin - json” import resolve from“ rollup - plugin - node - resolve” import commonjs from‘ rollup - plugin - commonjs’ export default { //input:”src/index.js”, //指定入口文件路径 //多入口打包 //input:[‘src/index.js’,’src/album.js’], input: { foo: ’src / index.js’, bar: ’src / ablum.js’ }, output: { //file:”dist/bundle.js”, //指定输出的文件名 //format:”iife”, //指定输出的格式 //下述是代码拆分需要使用的模式 dir: ’dist’, format: ’amd’ }, plugins: [ json(), resolve(), //rollup不能让node支持ESM规范,rollup通过resolve,让node支持ESM规范;webpack能让node支持ESM规范 commonjs(), babel({ babelHelpers: 'bundled' }) ] } (2)运行配置文件, rollup,按照以下顺序运行配置文件:rollup.config.mjs -> rollup.config.cjs -> rollup.config.js rollup --config my.config.js,运行配置文件:my.config.js 3、API,扩展Rollup本身或者进行一些高级操作 (1)rollup.rollup,参数为输入选项对象,返回一个Promise,该Promise解析为具有各种属性和方法的bundle对象 (2)rollup.watch,当它检测到磁盘上某个模块已经改变,它会重新构建bundle(捆) 4、其他,在模块化方面,browserify和node都用commonjs规范(require导入,module.exports导出),ES6用ESM规范(import导入,exports导出) 十六、各框架各工具各版本的发行时间 附、软件发布前的三个步骤 (1)内测,Alpha(α,阿尔法) (2)公测,Beta(β,贝塔) (3)正式发布,Gamma(γ,伽玛) 附、JS版本 (1)1995年,发布JavaScript语言;美国人布兰登·艾克创造 (2)1997年,发布ECMAscript1 (3)1999年,发布ECMAscript3 (4)2009年,发布ECMAscript5 2012年,发布TypeScript (5)2015年,发布ECMAscript6 附、JS其它框架 (1)SvelteJS,2016年创建,解决传统前端框架在运行时性能上的瓶颈 // /svelt/苗条的,纤细的;https://www.svelte.cn/ (2)SolidJS,2018年创建,制作交互式Web应用程序的JS框架 // /ˈsɒlid/坚固的,实心的;https://www.solidjs.cn/ A、3个核心API,createSignal、createMemo、createEffect,以createEffect为例,实现逐词插入数据,类似于发报效果 import { createEffect, onMount } from 'solid-js'; import { Marked } from '@ts-stack/markdown'; type Props = { message: MessageType; chatflowid: string; }; Marked.setOptions({ isNoP: true }); export const BotBubble = (props: Props) => { let botMessageEle: HTMLDivElement | undefined; onMount(() => { }); var i = 0; createEffect(() => { if (botMessageEle) { let before = props.message.message; let reg = /"answer": "(.+)",/g; let after = []; if(!reg.test(before)){ after.push(before) }else{ before.replace(reg,function(regAll,A1){ after.push(A1) }); } const interval = setInterval(() => { botMessageEle.innerHTML = Marked.parse(after[i]); i++; if(i>after.length-2)clearInterval(interval) },100); } }); return ( <div class="flex flex-col justify-start"> {props.message.message && ( <span ref={botMessageEle} /> )} </div> ); }; B、3个生命周期,onMount(可以在此处向后台请求数据)、onCleanup、onError C、PascalCase,帕斯卡命名法,将变量的所有单词的首字母大写 D、splitProps,属性的使用 //以下案例来源,chat-embed项目下ShortTextInput.tsx文件 //Omit<Type, Keys>TypeScript创建新类型,从现有类型(Type)中排除指定属性(Keys) import { splitProps } from 'solid-js'; import { JSX } from 'solid-js/jsx-runtime'; type ShortTextInputProps = { ref: HTMLInputElement | HTMLTextAreaElement | undefined; onInput: (value: string) => void; fontSize?: number; disabled?: boolean; } & Omit<JSX.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onInput'>; export const ShortTextInput = (props: ShortTextInputProps) => { const [local, others] = splitProps(props, ['ref', 'onInput']); const handleInput = (e) => { if (props.ref) { e.currentTarget.scrollTo(0, e.currentTarget.scrollHeight); local.onInput(e.currentTarget.value); } }; return ( <textarea ref={props.ref} onInput={handleInput} {...others} /> ); }; //以下案例来源,chat-embed项目下TextInput.tsx文件 import { ShortTextInput } from './ShortTextInput'; export const TextInput = (props: Props) => { let inputRef: HTMLInputElement | HTMLTextAreaElement | undefined; return ( <ShortTextInput ref={inputRef as HTMLTextAreaElement} onInput={handleInput} /> ); }; 1、angular版本,Google发行 (1)AngularJS,首版,2009年 (2)AngularJS,1.0版,2010年, (3)Angular,2.0版,2016年09月 (4)Angular,4.0版,2017年03月 (5)Angular,5.0版,2017年11月 (6)Angular,6.0版,2018年05月 (7)Angular,7.0版,2018年10月 (8)Angular,8.0版,2019年05月 (9)Angular,9.0版,2020年02月 (10)Angular,10.0版,2020年06月 (11)Angular,11.0版,2020年11月 (12)Angular,12.0版,2021年05月 (13)Angular,13.0版,2021年12月 (14)Angular,14.0版,2022年06月 (15)Angular,15.0版,2022年11月 (16)Angular,16.0版,2023年05月 2、vue版本 来源,https://github.com/vuejs/core/blob/main/CHANGELOG.md (1)vue,1.0.0版,2015年10月27日 (2)vue,2.0.0版,2016年10月01日 (3)vue,3.0.0版,2020年01月04日,预发布 A、vue,3.0.0-beta.20,2020年07月08日,此前setup函数作为组件的一个配置项 B、vue,3.0.0-beta.21,2020年07月14日,此后增加了 <script setup> 的实验特性 (4)vue,3.0.0版,2020年09月18日,正式发布 (5)vue,3.1.0版,2021年06月07日 (6)vue,3.2.0版,2021年08月09日,<script setup> 正式使用 (7)vue,3.3.0版,2023年05月11日 (8)vue,3.4.0版,2023年12月29日 附、vue-cli版本 (1)vue-cli,3.0.0版,2018年08月10日 (2)vue-cli,4.0.0版,2019年10月16日 (3)vue-cli,4.5.0版,2020年07月24日,开始默认使用vue3 (4)vue-cli,5.0.0版,2022年02月17日 附、uni-app版本 (1)2018年8月,uni-app1.0.0 版本发布, (2)2021年9月,uni-app2.0.0 版本发布, (3)2023年1月,uni-app3.0.0 版本发布, 3、react版本,FaceBook发行 附、2016年10月,发布Next.js,它是React的框架 来源,https://github.com/facebook/react/releases react中国,https://react.docschina.org 所有版本简介,https://github.com/facebook/react/blob/main/CHANGELOG.md#1702-march-22-2021 reactdom版本,https://cdn.bootcdn.net/ajax/libs/react-dom/16.6.0/cjs/react-dom.development.js (1)React,0.3.0版,2013年05月29日 (2)React,0.14.8版,2016年03月29日 (3)React,15.0.0版,2016年04月07日 A、15.1.0版,出现错误边界,error boundaries B、15.2.3版,出现纯函数组件,PureComponent (4)React,16.0.0版,2017年09月26日, A、新增componentDidCatch(--记录错误--) B、新增纤维fiber架构, C、弃用旧虚拟DOM, D、解决了递归调用无法中断和卡顿掉帧的问题 (5)React,16.3.0版,2018年03月29日, A、沿用旧生命周期componentWillMount,componentWillReceiveProps,componentWillUpdate, B、新增新生命周期getDerivedStateFromProps,getSnapshotBeforeUpdate, C、沿用方案、新增方案只能二选一, D、组件自身state更新,shouldComponentUpdate()>render()>getSnapshotBeforeUpdate()>componentDidUpdate() E、传递过来的props更新,getDerivedStateFromProps()>shouldComponentUpdate()>render()>getSnapshotBeforeUpdate()>componentDidUpdate() (6)React,16.4.0版,2018年06月24日, A、新增Suspense(--组件--) (7)React,16.6.0版,2018年10月23日, A、新增getDerivedStateFromError(--处理错误--) B、新增React.memo() C、新增React.lazy() (8)React,16.8.0版,2019年02月06日,见本页-Hooks详解- A、新增钩子函数Hooks,可以 B、避免组件继承React实例 C、实现状态管理 D、弃用生命周期 (9)React,17.0.0版,2020年10月20日 A、并没有添加任何面向开发人员的新特性 (10)React,18.0.0版,2022年03月29日, A、新增useTransition(--处理过渡--) (11)React,18.3.1版,2024年04月26日,最新版本 4、node版本(内含npm) 来源,https://nodejs.org/zh-cn/download/releases/ 来源,https://nodejs.org/en/blog/release/page/1 来源,https://pnpm.io/zh/motivation (1)版本发布 A、2009年,node出现 B、2010年,npm出现(Node Package Manager,node包管理器) C、2011年,Windows版node出现 D、2012年,原作者离开 E、2015年,node发布4.0.0版、5.0.0版,内含npm的2.14.2版、3.3.6版 F、2016年,node发布6.0.0版、7.0.0版,内含npm的3.8.6版、3.10.8版 G、2017年,node发布8.0.0版、9.0.0版,内含npm的5.0.0版、5.5.1版 H、2018年,node发布10.0.0版、11.0.0版,内含npm的5.6.0版、6.4.1版 I、2019年,node发布12.0.0版、13.0.0版,内含npm的6.9.0版、6.12.0版 node13.2.0开始支持ES6模块,此后无需用vue-loader把.vue文件转化为ES6模块 J、2020年,node发布14.0.0版、15.0.0版,内含npm的6.14.4版、7.0.2版 K、2021年,node发布16.0.0版、17.0.0版,内含npm的7.10.0版、8.1.0版 L、2022年,node发布18.0.0版、19.0.0版,内含npm的8.6.0版、8.19.2版 M、2023年,node发布20.0.0版、21.0.0版,内含npm的9.6.4版、10.2.0版 N、2024年,node发布22.0.0版、2x.0.0版,内含npm的10.5.1版、1x.x.x版 (2)普通多版本node,安装、关联、切换、查看方法: A、安装: a、下载各版本的.zip,分别解压到“C:\Program Files\node10”下 b、下载各版本的.exe,逐个先安装至“D:\”下,再粘贴到“C:\Program Files\node10”下 B、关联: a、前往,桌面>右键计算机>属性>高级系统设置>环境变量;双击(系统变量的)Path></script> b、新建-输入“C:\Program Files\node10”-确定,重复此步骤...> c、确定>确定 C、切换: a、前往,桌面>右键计算机>属性>高级系统设置>环境变量;双击(系统变量的)Path> b、点击“C:\Program Files\node10”-点击上移或下移 D、查看版本: a、不能在切换前开启的命令框, b、只能在切换后开启的命令框->输入“node -v”->回车, (3)nvm多版本node,安装、切换、查看方法: A、下载与安装 a、下载,https://gitcode.com/gh_mirrors/nv/nvm-windows/releases b、安装,C:\Users\Haier\AppData\Roaming\nvm B、设置镜像 a、nvm node_mirror https://npmmirror.com/mirrors/node/ b、nvm npm_mirror https://npmmirror.com/mirrors/npm/ C、常见命令 a、nvm -v,查看nvm版本号;node -v,查看当前node版本号 b、nvm list available,能安装的版本(不完整) c、nvm install 16.20.0,安装版本-node@16.20.0 d、nvm use 16.20.0,使用版本-node@16.20.0 e、nvm ls(list),已安装版本列表,前面带*号的为在用版本 D、上面命令,在vscode编辑器里运行更有效,也可以“以管理员身份运行” a、点击任务栏上的放大镜,右键“命令提示符”,点击“以管理员身份运行” b、点击任务栏上的放大镜,输入cmd,点击“以管理员身份运行” 5、webpack版本 (1)Webpack1,2013 (2)Webpack2,2016,支持ES Module,支持Tree-Shaking,将没用到的代码剔除 (3)Webpack3,2017.06.20,Scope Hoisting(作用域提升)和Magic Comment(魔法注释)等 (4)Webpack4,2018.02.25,mode属性,WebAssembly(字节码格式),支持多种模块类型,0配置等 (5)Webpack5,2020.10.10,优化缓存,改善Tree-Shaking等等 附、webpack及类似(JS打包工具,构建工具,自动化工具) (1)Webpack,2013年首发的前端构建工具 (2)Gulp,2015年首发的前端构建工具 (3)vite,2019年首发的前端构建工具 6、国外AI (1)codeium,https://codeium.com/playground (2)codegeex,https://codegeex.cn/zh-CN/playground (3)chatGPT,http://chat.178le.net/index A、2022年11月30日,美国OpenAI发布的聊天机器人程序 B、全名:Chat Generative Pre-trained Transformer C、汉译:人工智能技术驱动的自然语言处理工具 D、直译:聊天生成的、经过训练的、改革者 E、Copilot Hub:基于ChatGPT创建个人的知识库AI,app.copilothub.co 7、国内AI (1)豆包,https://www.doubao.com/chat A、2023年8月,字节跳动的人工智能机器人“豆包”开始测试 B、2024年5月,豆包App总下载量已达1亿次,价格相比同行便宜99.3% C、2024年8月,豆包上线音乐生成功能 D、支持网页Web平台,iOS平台,安卓平台 E、豆包的功能 a、聊天机器人 b、写作助手 c、学习助手 d、回答各种问题 (2)文心一言,https://yiyan.baidu.com/ (3)智谱清言,https://chatglm.cn/main/alltoolsdetail (4)Kimi,https://kimi.moonshot.cn/ (5)通义千问,https://qianwen.aliyun.com/ (6)讯飞星火,https://xinghuo.xfyun.cn/desk (7)腾讯元宝,https://yuanbao.tencent.com/chat/naQivTmsDa (8)百小应,https://ying.baichuan-ai.com/chat 十七、神策(本文档基于1.26.5版本) 注意,先做项目(了解源码的功能),再看源码(了解功能的实现) 作用,分析用户的行为 来源,https://manual.sensorsdata.cn/sa/latest/zh_cn/tech_sdk_client_web_use-7545024.html 1、集成文档 (1)自动生成 (2)同步载入 (3)CommonJS规范加载 (4)ES6模块化引入 (5)AMD规范加载 2、基础api介绍(属性-注册、获取、设置、上报) (1)SDK初始化参数,16项 (2)注册公共属性 // 以下项目旧代码备份 // var i=0 // sensors.init({ // server_url: server_url, //神策数据接收地址 // //web_url: web_url, //神策分析后台地址,神策1.10及以上版本,不需要配置这个参数, // is_track_single_page: true, //单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发 $pageview 事件 // use_client_time: true, // send_type: 'beacon', // // heatmap: { // // clickmap: 'default', //(1/3)开启点击图,自动采集a input button textarea 四种元素的$WebClick事件,'not_collect'表示关闭 // // scroll_notice_map: 'default', //(2/3)开启触达图,自动采集$WebStay事件,'not_collect'表示关闭 // // }, // show_log: true, //console会打印采集信息 // // preset_properties: { //子配置项 true 表示采集,false 表示不采集,未设置的参数取默认值 // // latest_traffic_source_type: false, //是否采集 $latest_traffic_source_type 最近一次流量来源类型 // // latest_search_keyword: false, //是否采集 $latest_search_keyword 最近一次搜索引擎关键字 // // latest_referrer: false, //是否采集 $latest_referrer 最近一次前向地址 // // }, // }); // sensors.registerPage({ // current_url: location.href, // referrer: document.referrer, // description1: 'server_url字段为空或非神策地址,在开发者的控制台,依然可以看', // description2: '到神策插件采集的数据,只是神策服务器接收不到该数据,没法分析!', // prop_number_: function() { // return ++i; // }, // }); // sensors.quick('autoTrack'); //(3/3)用于采集$pageview事件 (3)获取预置属性 sensors.quick('isReady',function(){ var presetProperties = sensors.getPresetProperties(); console.log( presetProperties ); }); (4)设置用户属性 A、直接设置用户的属性,如果存在则覆盖。 sensors.setProfile({email:'xxx@xx'}); B、如果不存在则设置,存在就不设置。 sensors.setOnceProfile({email:'xxx@xx'}); C、给数组属性添加值 sensors.appendProfile({catrgory: ['玉米','白菜']}); sensors.appendProfile({catrgory: '玉米'});//给 category 增加一个值 D、对当前用户的属性做递增或者递减 sensors.incrementProfile({'navClick': -1});//表示navClick递减 E、删除当前用户及他的所有属性 sensors.deleteProfile(); F、删除当前用户的一些属性 sensors.unsetProfile(['email','location']); sensors.unsetProfile('email'); (5)物品元数据上报 A、直接设置一个物品,如果已存在则覆盖 sensors.setItem("food","2",{name:"玉米",flavour:"甜"}); setItem(item_type,item_id,properties) 除物品ID与物品所属类型外,其他物品属性需在properties中定义 物品属性中,属性名称与属性值的约束条件与事件属性相同 item_type:必选 item_id:必选 properties:可选 B、sensors.deleteItem("food","2"); deleteItem(item_type,item_id) 如果物品不可被推荐需要下线,删除该物品即可,如不存在则忽略 除物品ID与物品所属类型外,不解析其他物品属性 item_type:必选 item_id:必选 3、全埋点(标签埋点、属性埋点) 附1、heatmap相关参数,https://manual.sensorsdata.cn/sa/latest/zh_cn/tech_sdk_client_web_all_use-7545310.html 附2、server_url获取步骤,登录神策-基本设置-数据接入-(客户端埋点)生成导入代码-生成-server_url 附3、示例,全埋点三个事件(元素点击$WebClick、视区停留$WebStay、页面浏览$pageview),其中$WebClick默认采集4种元素(a input button textarea) <script> //var sensors = window['sensorsDataAnalytic201505']; import sensors from 'sa-sdk-javascript'; var server_url = 'https://10.50.16.15/api'; var web_url = 'https://10.50.16.15/api'; var i=0; sensors.init({ server_url: server_url,// 数据接收地址 web_url: web_url,// 神策分析后台地址,神策1.10及以上版本,不需要配置这个参数, is_track_single_page: true, // 单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发 $pageview 事件 use_client_time: true, send_type: 'beacon', heatmap: { clickmap:'default',//(1/3)开启点击图,$WebClick事件默认采集4种元素,'not_collect'表示关闭 scroll_notice_map:'default',//(2/3)开启视区停留(触达图),自动采集$WebStay事件,'not_collect'表示关闭 loadTimeout: 3000, collect_url: function(){ //如果只采集首页 if(location.href === 'example.com/index.html' || location.href === 'example.com/'){ return true; } }, //此参数针对预置 $WebClick 事件(包括 quick('trackHeatMap') quick('trackAllHeatMap') 触发的)生效。 collect_element: function(element_target){ // 如果这个元素有属性sensors-disable=true时候,不采集。 if(element_target.getAttribute('sensors-disable') === 'true'){ return false; }else{ return true; } }, //此参数针对预置 $WebClick 事件(包括 quick('trackHeatMap') quick('trackAllHeatMap') 触发的)生效。 custom_property: function( element_target ){ //比如您需要给有 data=test 属性的标签的点击事件增加自定义属性 name:'aa' ,则代码如下: if(element_target.getAttribute('data') === 'test'){ return { name:'aa' } } }, collect_input: function(element_target){ if(element_target.id === 'a'){ //如果元素的 id 是a,就采集这个元素里的内容。 return true; } }, element_selector: 'not_use_id', renderRefreshTime: 1000 }, scrollmap: { collect_url: function(){//如果只采集首页 if(location.href === 'example.com/index.html' || location.href === 'example.com/'){ return true; } }, }, show_log: true, // console会打印采集信息 }); sensors.quick('autoTrack'); //(3/3)自动采集$pageview事件 sensors.quick('autoTrack', { //添加额外的属性 platform: 'h5' }) </script> (1)扩展至div元素,采集规则为 A、div为叶子结点(无子元素)时采集div的点击 B、div中有且只有样式标签(['mark','strong','b','em','i','u','abbr','ins','del','s','sup'])时,点击div或者样式标签都采集div的点击 heatmap:{ collect_tags:{ div: true } } (2)扩展至任意元素,示例 heatmap: { clickmap:'default', collect_tags: { div: { //div通过配置最多可以采集3层嵌套 max_level: 1 //默认是1,即只支持叶子div。可配置范围是[1, 2, 3],非该范围配置值,会被当作1处理。 }, get_vtrack_config: true, //无限层级的div li: true, img: true //...其他标签 } } (3)扩展至特殊属性,示例 <div data-sensors-click>我是测试元素</div> <li data-sensors-click>我是测试元素</li> (4)扩展至自定义属性,示例 heatmap: { clickmap:'default', track_attr: ['prop1', 'prop2', "prop3"], } <p prop1>prop1</p> <p><span prop2>prop2</span></p> <p><strong prop3>prop3</strong></p> (5)代码埋点(这是局部埋点) 示例1,jQuery <div id="submit_order">提交订单</div> <script type="text/javascript"> $('#submit_order').on('click', function() { //代码埋点 sensors.quick('trackHeatMap', this, { //触发元素点击事件 customProp1: 'test1', //如果需要添加自定义属性需要将SDK升级到 1.13.7 及以上版本。 customProp2: 'test2' }); }); </script> 示例2,vue <div v-on:click="track">点击</div> <script> export default { methods: { track: function(event) {//代码埋点 sensors.quick('trackHeatMap', event.target, {//触发元素点击事件 customProp1: 'test1', //如果需要添加自定义属性需要将SDK升级到 1.13.7 及以上版本。 customProp2: 'test2' }); } } } </script> 4、高级功能 附、高级功能清单 A、属性插件化, B、批量发送, C、预置属性是否采集, D、关闭页面时发送数据丢失的解决方案, E、单页面中事件的自动采集 (1)属性插件化, 作用,给指定的事件添加、修改或删除属性, 实现,registerPropertyPlugin,包含properties和isMatchedWithFilter 如果不配置后者,配置的 properties 方法始终执行 如果配置了后者,仅当该方法返回 true 时,配置的 properties 方法才会得到执行 示例如下 A、直接修改 sensors.registerPropertyPlugin({ //对所有类型数据,修改属性值,这种用法会引发意想不到的情况 properties: function(data){ data.properties['aaa'] = 'bbb'; } }); sensors.registerPropertyPlugin({ //删除所有事件中的 platform 属性 properties: function(data){ delete data.properties['platform']; } }); sensors.registerPropertyPlugin({ //直接在properties里进行筛选和属性修改 properties: function(data){ if(data.event === '$pageview'){ data.properties['$url'] = 'http://xxxx'; } } }); B、先筛选后修改 sensors.registerPropertyPlugin({ //修改事件名为 $pageview 下的 $url 属性 isMatchedWithFilter: function(data){ return data.event === "$pageview"; } properties: function(data){ data.properties['$url'] = 'http://xxx'; } }); sensors.registerPropertyPlugin({ isMatchedWithFilter: function(data){ return data.type.slice(0, 4) === 'item' || data.type.slice(0, 7) === 'profile'; } properties: function(data){ delete data.properties['aaa'] = 'bbb'; } }); (2)批量发送 示例如下 sensors.init({ batch_send:true,//开启批量发送 batch_send:{//或者 datasend_timeout: 6000, //一次请求超过多少毫秒的话自动取消,防止请求无响应。 send_interval: 6000, //间隔多少毫秒发一次数据。 storage_length: 200 //存储localStorage条数最大值,默认:200。如localStorage条数超过该值,则使用image方式立即发送数据。v1.24.8 以上支持。 }, }); 写入策略 触发事件就写入localStorage 发送策略 定时触发发送 遇到$pageview、使用quick('autoTrack')、使用$SignUp,立即存储并且发送 重复策略 必须请求success后,才会删除数据,不然会一直请求,直到数据满一定数量 (3)预置属性是否采集 sensors.init({ preset_properties: { //子配置项 true 表示采集,false 表示不采集,未设置的参数取默认值 latest_utm: true, //是否采集 $latest_utm 最近一次广告系列相关参数 latest_traffic_source_type: true, //是否采集 $latest_traffic_source_type 最近一次流量来源类型 latest_search_keyword: true, //是否采集 $latest_search_keyword 最近一次搜索引擎关键字 latest_referrer: true, //是否采集 $latest_referrer 最近一次前向地址 latest_referrer_host: false, //是否采集 $latest_referrer_host 最近一次前向地址,默认值先true后false latest_landing_page: false, //是否采集 $latest_landing_page 最近一次落地页地址,默认值false。 url: true, //是否采集 $url 页面地址作为公共属性,默认值先false后true title: true, //是否采集 $title 页面标题作为公共属性,默认值先false后true }, source_channel:'',//默认获取的来源是根据utm_source等ga标准来的 //utm_source,指定流量的来源,例如搜索引擎、社交媒体网站等 //utm,是Urchin(小脏孩,免费的分析工具) Tracking Module 的简称 }); (4)关闭页面时发送数据丢失的解决方案 A、改为服务端发送事件 在服务端中埋点采集 B、采集跳转后页面的$pageview事件 给A页面的a标签的href属性添加某个参数,如B页面www.xxx.com?urlfrom=123, 跳转到B页面后,采集这个页面的$pageview事件,在神策后台中查看Web浏览事件,根据url是否包含urlfrom参数来筛选结果 C、使用setTimeout // 点击链接执行 function targetLinkIcon( url ){ setTimeout(function(){ //延迟跳转页面,给 SDK 发送数据提供时间 window.location.href = url; },500); sensors.track('demo',{}); //神策自定义事件的方法 } D、beacon的方式发送数据 配置 sensors.init({ send_type: 'beacon', }); 原理说明 来源,https://blog.csdn.net/Ed7zgeE9X/article/details/131545832 Beacon API是HTML5提供的新型浏览器API,可以在不影响当前页面加载和性能的情况下,在浏览器后台异步发送数据 借助Beacon API,开发人员可以在页面卸载或关闭时向服务器发送数据,从而实现一些监控和日志记录功能 //以下前端代码 var data = "Hello, Beacon API!"; var url = "https://example.com/endpoint"; navigator.sendBeacon(url, data); //导航员.发送信标 //以下后端代码 app.post('/endpoint', function(req, res) { var data = req.body; // 处理接收到的数据 // ... res.sendStatus(200); // 返回响应状态码 }); (5)单页面中事件的自动采集 A、自动模式 sensors.init({ is_track_single_page: true, is_track_single_page: function (){ return true 时候,使用默认发送的 $pageview return false 时候,不执行默认的 $pageview return {} 时候,把对象中的属性,覆盖$pageview里的默认属性 } }); C、手动模式 在 react 中可以在全局的 onUpdate 里来调用 onUpdate: function(){ sensors.quick('autoTrackSinglePage'); } vue 项目在路由切换的时候调用 router.afterEach((to,from) => { Vue.nextTick(() => { sensors.quick("autoTrackSinglePage"); }); }); //注意:vue下因为首页打开时候就会默认触发页面更新,所以需要去掉默认加的 sa.quick('autoTrack')。 //此方法也可添加自定义属性,sensors.quick("autoTrackSinglePage",{platForm:"H5"}); 5、安全合规 (1)安全, 本地存储加密,不支持埋点数据加密 sensors.init({ encrypt_cookie: true //开启cookie加密配置,默认false }); (2)合规, 延迟初始化 if(同意隐私条款){ sensors.init({}); sensors.quick('autoTrack'); } 动态开启/关闭采集,必须在init后调用 sensors.init({}); sensors.disableSDK()// 禁用API执行 sensors.enableSDK()// 恢复API执行 6、用户关联 (1)简易用户关联, 方法:sensors.login("登录 ID"); 时机: A、用户注册成功时 B、用户登录成功时 C、已登录用户每次启动App时 获取匿名ID sensors.quick('isReady',function(){ var anonymousID = sensors.quick('getAnonymousID'); }); 修改匿名ID sensors.identify(id, true): 会把这个id保存在浏览器的cookie中,该域名下的页面都会默认使用这个id (2)全域用户关联 方法 sensors.login("登录 ID"); 时机 A、用户注册成功时 B、用户登录成功时 多用户ID关联, sensors.bind("$identity_mobile","187****8991"),后续采集的事件,均包含缓存的ID信息 第1个参数从详细的预置id key列表中获取,https://manual.sensorsdata.cn/sa/latest/zh_cn/tech_sdk_client_web_idm3-109576372.html 第2个参数为对应的关联用户ID 多用户ID取消关联, sensors.unbind("$identity_mobile","187****8991") 重置匿名ID, sensors.resetAnonymousIdentity(); sensors.resetAnonymousIdentity('id-xxxxxxx-xxxxx'); // 修改为指定的匿名ID 获取全域用户的ID, sensors.getPresetProperties() sensors.quick('isReady',function(){ sensors.getPresetProperties() }); 7、插件集成 (1)使用内置插件, 内置插件 Amp、SensorsChannel、Deeplink、PageLeave、PageLoad、RegisterPropertyPageHeight、SiteLinke 使用 sensors.use('PageLeave', option); sensors.init({ ...初始化参数 }) (2)使用外置插件, 外置插件 AesEncryption、Exposure、SessionEvent、SiteLinkerConcatUtm、WechatWebViewChannel、SensorsABTest、Popup(H5版)WebPopup(Web版)、GeneralEncryption、CustomEventsSender、SfInstantEvent、ChannelUtm、SMEncryption 使用 import sessionEvent from '/dist/web/plugin/session-event/index.es6.js'; sensors.use(sessionEvent); sensors.init({ ...初始化参数 }) 8、多域名打通 (1)示例 sensors.init({}); sensors.use('SiteLinker', { linker: [ // 在神策分析环境中实现用户统一,从而实现跨域打通 { part_url: 'sensorsdata.cn', after_hash: false }, { part_url: 'example.com', after_hash: false } ], re_login: true, //如果从已登录的页面跳转过来,即使当前网页已经登录,当前网页仍然会以之前网页的登录id再次登录 } ); sensors.quick('autoTrack'); (2)打通结果 (3)原理说明 A、浏览器中的Cookie包含用户id和域名 B、多域名间共享用户id C、只有a标签跳转可以实现跨域打通 (4)实施方案 A、linker参数的part_url属性配置‘需要打通的网域’ B、神策检查本网域中的a标签的链接,如果链接包含part_url,将从Cookie中提取本网域下的Distinct ID拼接到链接上 C、跳转到目标网域后,目标网域截取网址中的链接器_sasdk参数,用其替换自身的Distinct ID D、反过来也一样 9、渠道追踪与广告(以下数据自动生成) 注意,从搜索引擎的搜索结果向目标网页跳转时,如果搜索引擎不携带相关参数,会导致下列数据的部分字段为空 (1)用户相关属性 (2)事件相关属性 (3)流量来源类型 (4)搜索引擎关键词 (5)场景示例 (6)常见问题 10、曝光采集, 包含名称、配置、属性 (1)初始化与注销 初始化 import Exposure from '/dist/web/plugin/exposure/index.es6'; var exposure = sensors.use(Exposure, { area_rate: 0, stay_duration: 2, repeated: true }); 注销 exposure.removeExposureView(document.getElementById('exposure_ele')) (2)注册曝光元素 方案1 <div data-sensors-exposure-event-name="home_top_banner" data-sensors-exposure-config-area_rate="1" data-sensors-exposure-config-stay_duration="2" data-sensors-exposure-config-repeated="true" data-sensors-exposure-property-propA="valueA" data-sensors-exposure-property-propB="valueB" ></div> 方案2 <div id="exposure_div"></div> var exposureDiv = document.getElementById("exposure_div"); exposureDiv.setAttribute("data-sensors-exposure-event-name", "exposure_ele"); //1、名称 exposureDiv.setAttribute( //设置曝光元素配置及自定义属性 "data-sensors-exposure-option", JSON.stringify({ config: { //2、配置 area_rate: 0.5, stay_duration: 0, repeated: true }, properties: { //3、属性 d: "abc", e: false }, listener: { //4、曝光的回调 shouldExpose: function (ele, properties) { //是否触发曝光事件 // ele 为当前曝光的元素 // properties 为曝光元素的元素信息及该元素的曝光自定义属性 // 触发曝光事件则返回 true // 不触发曝光事件则返回 false return true; }, didExpose: function (ele, properties) { //已触发曝光回调 // ele 为当前曝光的元素 // properties 为曝光元素的元素信息及该元素的曝光自定义属性 } } }) ); 11、谷歌AMP (1)说明,AMP提供了可分析用户行为数据的<amp-analytics>元素,通过该元素实现对神策埋点数据的采集 (2)集成,在head中引入拓展组件<amp-analytics>的脚本 <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script> (3)采集,在body中引入<amp-analytics>标签,标签type="sensorsanalytics",通过编辑标签内部的json配置,来进行行为数据采集 <amp-analytics type="sensorsanalytics" id="sensorsanalytics1"> <script type="application/json"> "requests": { "event": "https://jssdkdata.debugbox.sensorsdata.cn/sa?project=liangshuang" //数据接收地址 }, "triggers": { //下面的key相当于上面的$WebClick、$WebStay、$pageview "trackPageview": { //页面浏览事件采集 "on": "visible", "request": "event", "vars":{ "event":"amp_pageview", //自定义事件名称 "amp_properties":"%7B%22platform%22%3A%22amp%22%2C%22color%22%3A%22red%22%7D" //自定义属性 } }, "id_test1": { //自定义事件采集 "on": "click", "selector": "#test1", //点击事件需要监听的元素选择器 "request": "event", "vars":{ "event":"click_test1", //自定义事件名称 "amp_properties":"%7B%22platform%22%3A%22amp%22%2C%22color%22%3A%22red%22%7D" //自定义属性 } }, "class_test2": { //自定义事件采集 "on": "click", "selector": ".test2", //点击事件需要监听的元素选择器 "request": "event", "vars":{ "event":"click_test2", //自定义事件名称 "amp_properties":"%7B%22platform%22%3A%22amp%22%2C%22color%22%3A%22red%22%7D" //自定义属性 } } } </script> </amp-analytics> (4)amp_properties值的生成步骤 var prop = { platform:'amp', color:'red' } var amp_properties = encodeURIComponent(JSON.stringify(prop)) (5)AMP网页的三种访问方式 AMP查看器访问 代理/缓存访问 普通浏览器直接访问 (6)AMP页面和非AMP页面的用户统一 sensors.init({}); sensors.use('Amp'); //引入AMP插件 sensors.quick('autoTrack'); (7)检测AMP集成是否成功 触发一条埋点事件,查看network是否有sa.gif的请求发出 12、深度链接deeplink (1)需求,客户希望通过H5将用户引流至移动 App,借助于深度链接可以提高活动推广的效果 13、常见问题FAQ 附、常见问题解答(frequently-asked questions,简称FAQ)是使新用户熟悉规则的一种方法 (1)单页面的页面标题$title问题 router.beforeEach((to, from, next) => { document.title = '新页面的 title 值'; next() }) (2)异步集成时,使用“内置(但)未使用插件” sensors.quick('isReady',function(){ sensors.use('PageLeave') sensors.quick('autoTrack') }) 附、采集数据的含义 注释参考1;https://manual.sensorsdata.cn/sa/latest/zh_cn/%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F-149553285.html 注释参考2;https://manual.sensorsdata.cn/sa/latest/zh_cn/id-key-150668129.html 注释参考3;https://manual.sensorsdata.cn/sa/latest/zh_cn/%E9%A2%84%E7%BD%AE%E5%B1%9E%E6%80%A7%E6%80%BB%E8%A1%A8%E6%A0%BC-152305885.html 注释参考4;https://manual.sensorsdata.cn/sa/latest/zh_cn/kafka-154632771.html [web-sdk-log]: { "identities": { //identities:全域用户关联业务中用户标识字段,可包含多个用户标识,具体可以参考全域用户关联, //https://manual.sensorsdata.cn/sa/latest/zh_cn/%E6%A0%87%E8%AF%86%E7%94%A8%E6%88%B7%E2%80%94%E2%80%94%E5%85%A8%E5%9F%9F%E7%94%A8%E6%88%B7%E5%85%B3%E8%81%94-150667578.html "$identity_cookie_id": "18dcb37ea531b29-088c1d7f7926fa8-1e525637-2104200-18dcb37ea54bd9"//Web cookie ID }, "distinct_id": "18dcb37ea531b29-088c1d7f7926fa8-1e525637-2104200-18dcb37ea54bd9",//类型是字符串,对用户的标识,对未登录用户,可以填充设备标识、CookieID 等,对于登录用户,则应该填充注册账号;这里的例子,假设是一个匿名用户,所以填充的是一个设备 ID; "lib": {//SDK "$lib": "js",//SDK 类型 "$lib_method": "code",//埋点方式 "$lib_version": "1.26.5"//SDK 版本 }, "properties": {//属性 "$timezone_offset": -480,//时区偏移量 "$screen_height": 1169,//屏幕高度 "$screen_width": 1800,//屏幕宽度 "$viewport_height": 363,//视区高度 "$viewport_width": 1752,//视区宽度 "$lib": "js",//SDK 类型 "$lib_version": "1.26.5",//SDK 版本 "$referrer": "http://test.cctv.com/",//前向地址,上一页面的$url信息 "$url": "http://test.cctv.com/#/home",//页面地址 "$url_path": "/",//页面路径 "$title": "智策",//页面标题 "$latest_referrer": "取值异常",//最近一次站外地址 "$latest_search_keyword": "取值异常",//最近一次搜索引擎关键词 "$latest_traffic_source_type": "取值异常",//最近一次流量来源类型 "$is_first_day": false,//是否首日访问 "$is_first_time": false,//是否首次触发事件 "$referrer_host": "test.cctv.com",//前向域名 "tmzone": 8,//时区 "b": "Chrome",//浏览器名称 "o": "MacIntel",//操作系统名称 "lng": "zh-CN",//操作系统语言 "ism": "不是移动端",//移动系统 "param": "#/home",//页面URL #后部分 "logtype": 1,//1-访问日志,2-交互日志 "single_page": true,//是否单页面应用 "show_log": true,//控制台是否显示日志 "v_id": "121212"//(自定义字段) }, "anonymous_id": "18dcb37ea531b29-088c1d7f7926fa8-1e525637-2104200-18dcb37ea54bd9",//login_id、anonymous_id:类型是字符串,对用户的标识,对未登录用户,只有 anonymous_id,而无 login_id 信息; "type": "track",//表示一条数据的具体操作(小结部分会详细介绍),track 表明是记录一个事件 "event": "$pageview",//事件名,需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,其中以 $ 开头的表明是系统的预置事件,自定义事件名请不要以 $ 开头,且 event 字段长度最大为 100;//事件($WebClick、$WebStay、$pageview) "time": 1709626986084,//类型是数值,事件发生的实际时间戳,精确到毫秒; "_track_id": 319126084,//前端 SDK track 的时候上报的随机值,用于去重判断,并不会写入 events 表 "_flush_time": 1709626986084//发送数据时的时间 } 十八、神策源码解读 注意,先做项目(了解源码的功能),再看源码(了解功能的实现) 注意,神策数据分为神策采集和神策分析,sd.use、sd.init、页面事件,三者一起添加监听事件 来源,https://juejin.cn/post/6974267640797724679 1、客户自有数据有三类 (1)前端操作,通过插件sensorsdata.full.js,采集、发送数据,该插件适用于PC、安卓、苹果、小程序等一切前端 (2)后端日志 (3)业务数据 2、插件采集数据的方式 (1)代码埋点 (2)全埋点 (3)可视化全埋点 (4)预置属性 3、在项目中使用 (1)在项目中引入采集插件,即1万多行的sensorsdata.full.js (2)在项目中插入采集代码,把采集数据发送给神策数据服务器 (3)用神策数据的账号、密码登录神策数据网站,查看采集数据 4、存储方式 (1)cookie (2)localStorage (3)sessionStorage 5、发送方式 (1)ajax,通过ajax异步POST请求将数据发送出去 (2)image,图片发送也是默认的发送方式,创建一个img标签,将数据放在请求的URL中,以问号传参的方式发送 (3)sendBeacon,浏览器会取消unload事件里的卸载、刷新、跳转逻辑,发出请求可以通过navigator.sendBeacon(导航员.发送信标)或同步ajax 6、全局变量 var sd = {}; //1,sensors-data var ee = {}; //6711,event-emit var sdk = new EventEmitter(); //6710,sensors-data-kit var _ = extend({}, W, business); //7545 7、ee,与事件相关, //以下在全局添加属性 ee.spa = spa; ee.initSystemEvent ee.EVENT_LIST ee.sdk = sdk; //6715 ee.sdk.on,监听事件 ee.sdk.emit,触发事件 8、sd,与框架相关 //以下在implementCore里添加属性, sd._ = _; sd.ee = ee; sd.on = eventEmitterFacade;//调用时,最终调用ee.sdk.on sd.use = use; sd.readyState = readyState; //以下在use里添加属性 sd.modules = sd.modules || {}; //存放插件 //以下暴露自身,并执行sd.init,同时注意区分变量和字符串 window[sensorsDataAnalytic201505] = sd; window['sensorsDataAnalytic201505'] = sd;//11415 sd.init(); 9、真实项目中的配置,默认自动开启日志输出功能方便调试, 来源,https://manual.sensorsdata.cn/sa/latest/tech_sdk_client_web_policy-109576387.html <script> window.sensors_data_pre_config = { is_compliance_enabled: true //是否同意启用 } </script> <script charset='UTF-8' src="./sensorsdata.min.js"></script> //sd.init,在插件同步加载结束时,不传参执行1次 <script> if(isClientAgree){//同意隐私条款 sensors.init({ //sd.init,启用插件时,传参执行1次 server_url: 'http://test-syg.datasink.sensorsdata.cn/sa?token=xxxxx&project=xxxxxx', is_track_single_page: true, //单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发$pageview事件 use_client_time: true, send_type: 'beacon', heatmap: { //是否开启点击图,'default'表示开启,自动采集 $WebClick 事件,可以设置'not_collect'表示关闭 clickmap: 'default', //是否开启触达图,'not_collect'表示关闭,不会自动采集 $WebStay 事件,可以设置'default'表示开启 scroll_notice_map: 'not_collect' } }); sensors.quick('autoTrack'); } </script> 10、页面监听与发射 附、sd.init = function(para) { ee.initSystemEvent(); sd.detectMode(); }; (1)监听 A、sd.init a、sd.detectMode(); B、function detectMode() {//检测模式 a、trackMode(); C、function trackMode() {//跟踪模式 a、listenSinglePage(); D、function listenSinglePage(trackFn) {//监听单个页面 if (sd.para.is_track_single_page) { spa.on('switch', function(last_url) {//监听(后出现,立即执行) var sendData = function(extraData) { extraData = extraData || {}; if (last_url !== location.href) {//浏览器前进或后退(用户触发) pageInfo.pageProp.referrer = getURL(last_url); var data = extend({ $url: getURL(), $referrer: getURL(last_url) }, extraData); isFunction(trackFn) ? trackFn(data) : sd.quick && sd.quick('autoTrack', data);//执行quick } }; if (typeof sd.para.is_track_single_page === 'boolean') { sendData(); } else if (typeof sd.para.is_track_single_page === 'function') { var returnValue = sd.para.is_track_single_page(); if (isObject(returnValue)) { sendData(returnValue); } else if (returnValue === true) { sendData(); } } }); } } E、function quick() { a、commonWays[arg0].apply(commonWays, arg1);//执行autoTrack F、autoTrack: function(para, callback) {//自动跟踪 a、sd.track( G、function track( a、saEvent.send({ H、send: function() { I、request: function(data, dataKeys) { J、function ajax$1(para) { K、function ajax(para) { (2)发射 A、sd.init a、ee.initSystemEvent(); B、ee.initSystemEvent = function() { addSinglePageEvent(function(url) { spa.emit('switch', url);//发射(先出现,不立即执行) }); }; C、function addSinglePageEvent(callback) { var current_url = location.href; //以下重定义方法,两种操作时执行,执行发射逻辑 var historyPushState = window.history.pushState; var historyReplaceState = window.history.replaceState; if (isFunction(window.history.pushState)) { window.history.pushState = function() {//浏览器前进或后退(用户触发) historyPushState.apply(window.history, arguments); callback(current_url);//执行spa.emit('switch',但不出现 current_url = location.href; }; } if (isFunction(window.history.replaceState)) { window.history.replaceState = function() { historyReplaceState.apply(window.history, arguments); callback(current_url); current_url = location.href; }; } //以下添加事件,五种操作时执行,执行发射逻辑 var singlePageEvent; if (window.document.documentMode) {//IE浏览器 singlePageEvent = 'hashchange'; } else {//非IE浏览器 singlePageEvent = historyPushState ? 'popstate' : 'hashchange'; // html5 : html5以下 } addEvent(window, singlePageEvent, function() { callback(current_url);//执行spa.emit('switch',但不出现 current_url = location.href; }); // (6)两种操作的作用,给“浏览器当前页签”添加(或修改)状态,以下是两种操作, // 附、它们是HTML5的一个API // A、pushState // B、replaceState // (7)五种操作的作用,给“浏览器当前页签”的“不同历史记录”激活,以下是五种操作 // A、浏览器的后退(向左) // B、浏览器的前进(向右) // C、history.back(向左) // D、history.forward(向右) // E、history.go(负左,正右,0当前) // (8)两种操作(手动)、五种操作(手动)、onpopstate(自动),三者之间的关系 // A、两种操作,只能改变当前页面状态,不会触发window.onpopstate(event)事件 // B、五种操作,与两种操作相关与否,都会触发window.onpopstate(event)事件 // C、五种操作,与两种操作无关时,还会触发http请求 } D、function addEvent(target, eventName, eventHandler, useCapture) { var register_event = function(element, type, handler) { if (useCapture === undefined && type === 'click') { useCapture = true; } if (element && element.addEventListener) { element.addEventListener(//被五种操作中的一种操作触发 type,//popstate function(e) { e._getPath = fixEvent._getPath; handler.call(this, e); }, useCapture ); } else { var ontype = 'on' + type; var old_handler = element[ontype]; element[ontype] = makeHandler(element, handler, old_handler, type); } }; register_event.apply(null, arguments); } (3)ajax的使用 附、定义,function ajax(para) { A、function ajax$1(para) { return ajax(para); } a、AjaxSend.prototype.start = function() { b、request: function(data, dataKeys) { c、var business = { B、var SADeepLink = { init: function init(sd) { this.sd._.ajax({ }, }; a、var index$5 = createPlugin$5(SADeepLink, 'Deeplink', 'sdkReady'); C、function debugPath(data) { _$6.ajax({}); } a、debugPath(JSON.stringify(data)); 附、hashchange,该事件在当前URL的锚部分发生修改时触发。它在本插件的应用可能如下 a、监听hashchange事件 b、当url上的hash改变时,触发hashchange事件,向后台发送相关数据 11、源码摘录 (1)extend,initPara--extend--each,外部数组遍历用forEach,内部对象遍历用for in,后面属性覆盖前面属性 var hasOwnProperty$1 = Object.prototype.hasOwnProperty; var hasOwnProperty$2 = Object.prototype.hasOwnProperty; function initPara(para) { extend(sdPara, para || sd.para || {}); sd.para = sdPara; } function extend(obj) { each(Array.prototype.slice.call(arguments, 1), function(source) { for (var prop in source) { if (hasOwnProperty$1.call(source, prop) && source[prop] !== void 0) { obj[prop] = source[prop]; } } }); return obj; } function each(obj, iterator, context) { if (obj == null) { return false; } if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context);//在函数的定义处执行 } else if (isArray(obj)) { for (var i = 0, l = obj.length; i < l; i++) { i in obj && iterator.call(context, obj[i], i, obj); // iterator定义里只有一个参数,这里传进去三个参数,在不改变iterator定义的情况下,第二、三个参数是无用的 } } else { for (var key in obj) { if (hasOwnProperty$2.call(obj, key)) { iterator.call(context, obj[key], key, obj); } } } } (2)与use相关 //溯源插件,index$d-createPlugin$d-wrapPluginInitFn$d(重写init) //执行插件,use(Plugin)->Plugin.init()-sd.on->eventEmitterFacade->ee[splitEvent[0]].on(splitEvent[1],callback)->ee.EVENT_LIST //注册拦截器,registerInterceptor //用2个for循环对2组插件进行遍历,并将每个插件放进sd.modules中 var builtinPlugins = [index$1, index$2, index$3, index$4, index$5, index$6, index$7, index$8, index$9, index$a, index$b, index$c, index$d, index$e, index$f, index$g, index$h, index$i, index$j, index$k]; // 共20项 var autoUsePlugins = [index, index$d, index$e, index$g, index$f, index$2, index$6, index$3, index$7, index$h, index$i, index$j, index$k]; // 共13项 for (var i = 0; i < builtinPlugins.length; i++) { var p = builtinPlugins[i]; if (sd._.isString(p.plugin_name)) { sd.modules[p.plugin_name] = p; //不需要执行plugin.init,把插件存入sd.modules } else { sd._.isArray(p.plugin_name) && sd._.each(p.plugin_name, function(v) { sd.modules[v] = p; }); } } for (i = 0; i < autoUsePlugins.length; i++) { sd.use(autoUsePlugins[i]); //需要执行plugin.init,把插件存入sd.modules } function use(plugin, option) { function initPlugin() { !curPlugin.plugin_is_init && curPlugin.init(sd, option); curPlugin.plugin_is_init = true; sd.modules = sd.modules || {}; sd.modules[curPlugin.plugin_name || 'unnamed_' + nonameCount++] = curPlugin; //把插件存入sd.modules return curPlugin; } return initPlugin(); } var index$d = createPlugin$d(utm, 'Utm', 'sdkAfterInitPara'); //utm是plugin的实参 function createPlugin$d(plugin, name, lifeCycle) { //添加版本 wrapPluginInitFn$d(plugin, name, lifeCycle); plugin.plugin_version = sdkversion_placeholder$e; return plugin; } function wrapPluginInitFn$d(plugin, name, lifeCycle) { //重写init if (name) { plugin.plugin_name = name; } if (lifeCycle && plugin.init) { var initFn = plugin.init; //存储旧的 plugin.init = function(sd, option) { //赋值新的 if ((sd.readyState && sd.readyState.state >= 3) || !sd.on) { return initPlugin(); } sd.on(lifeCycle, initPlugin); function initPlugin() { initFn.call(plugin, sd, option); } }; } return plugin; } (3)与sd.init相关 sd.init = function(para) { //8669 ee.sdk.emit('beforeInit'); if (sd.readyState && sd.readyState.state && sd.readyState.state >= 2) { return false; } if (is_compliance_enabled) { //是否同意启用 implementCore(true); //给sd添加属性 checkState(); //给sd的方法重新定义 } ee.initSystemEvent(); //初始化系统事件 sd.setInitVar(); //设置初始化变量 sd.readyState.setState(2); //设置全局状态 sd.initPara(para); //约130行,暂未了解 ee.sdk.emit('initPara'); ee.sdk.emit('afterInitPara'); ee.sdk.emit('initAPI'); ee.sdk.emit('afterInitAPI'); sd.detectMode(); //检测模式,暂未了解 iOSWebClickPolyfill(); //IOS系统兼容处理-可能 ee.sdk.emit('afterInit'); ee.sdk.emit('ready'); }; function implementCore(isRealImp) { if (isRealImp) { sd.events = events; sd.bridge = bridge; sd.SDKJSBridge = SDKJSBridge; sd.JSBridge = DeprecatedJSBridge; sd.store = store; sd.unlimitedDiv = unlimitedDiv; sd.customProp = customProp; sd.vtrackcollect = vtrackcollect; sd.vapph5collect = vapph5collect; sd.detectMode = detectMode; sd.registerFeature = registerFeature; sd.registerInterceptor = registerInterceptor; sd.commonWays = commonWays; registerFeature(new CoreFeature(sd)); registerFeature(new HeatCollectFeature(sd)); registerInterceptor('viewStage', heatCollectInterceptor); } var imp = isRealImp ? functions : saEmpty; for (var f in imp) { sd[f] = imp[f]; } sd._ = _; sd.on = eventEmitterFacade; sd.ee = ee; sd.use = use; sd.lib_version = sdkversion_placeholder; } function checkState() { each(methods, function(method) { var oldFunc = sd[method]; sd[method] = function() { if (sd.readyState.state < 3) { if (!isArray(sd._q)) { sd._q = []; } sd._q.push([method, arguments]); return false; } return oldFunc.apply(sd, arguments); }; }); } ee.initSystemEvent = function() { addSinglePageEvent(function(url) { spa.emit('switch', url); }); }; 十九、aplus.js 附、uri与url的区别 A、uri(Uniform Resource Identifier):统一资源标识符,用字符串标识某一资源的位置,示例如下 urn:issn:1535-3613:用于标识出版物的唯一标识号 tag:example.com,2008:3:用于标识博客文章的特定版本 外站类型、应用id、频道id、页面id B、url(Uniform Resource Locator):统一资源定位符,它是一种特殊类型的uri,标识了资源的具体位置,示例如下 http://www.example.com/path/file.html:用于标识特定的网络资源。 ftp://user:password@ftp.example.com/pub/file.txt:用于标识通过FTP协议访问的资源。 C、uri与url的关系, uri是一个广义的定义,它可以标识任何资源; url则是一种具体的uri,提供了一种标识资源的方式,特别是那些基于HTTP、HTTPS等协议可以通过浏览器进行访问的资源 附、meta标签 <html> <head> <meta charset="UTF-8"> <meta name="description" content="免费的 Web 教程"> <meta name="keywords" content="HTML,CSS,JavaScript"> <meta name="author" content="YK Investment"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="my-name" content="name的等号后面是属性值,由开发者确定"> <meta name="your-name" content="特定的属性值如keywords、viewport"> <meta name="your-name" content="会被网络爬虫或浏览器识别!"> </head> </html> <script> //name的等号后面是属性值,由开发者确定,特定的属性值如keywords、viewport,会被网络爬虫或浏览器识别! //js获取meta某name的content //方法1 var metas = document.getElementsByTagName('meta'); for (var i = 0; i < metas.length; i++) { if (metas[i].getAttribute('name') === 'my-name') { console.log(metas[i].getAttribute('content')); } } //方法2 var names = document.getElementsByName('your-name'); for (var i = 0; i < names.length; i++) { console.log(names[i].getAttribute('content')); } </script> 附、通用术语 (1)PV,页面浏览量或点击量,page view (2)UV,独立访客数,unique visitor (3)QPS,每秒查询率,Query Per Second (4)TPS,每秒吞吐量,Throughput Per Second,系统在单位时间内处理请求的数量 (5)RT,响应时间,Response Time 1、aplus.js说明 (1)源码地址,https://js.data.cctv.com/__aplus_plugin_cctv.js,aplus_plugin_aplus_u.js (2)使用方法,https://help.aliyun.com/product/194063.html,费了好大劲才找到 (3)二次封装,__aplus_plugin_cctv.js和aplus_plugin_aplus_u.js,是领导给我的 (4)相关域名 A、收数,https://log-api.aplus.emas-poc.com/,aplus.js的服务器 B、友盟,https://developer.umeng.com/docs/67963/detail/74526 C、分享,https://blog.naaln.com/2017/08/alibaba-data-track-1/ D、术语,https://zhuanlan.zhihu.com/p/650155427 2、aplus.js术语 来源,https://blog.csdn.net/qq_38397338/article/details/125246947 (1)SPM,全称超级位置模型,Super Position Model,记录和描述用户的具体点击位置信息,如外站类型、应用id、频道id、页面id A、spm-cnt,当前页面的SPM编码(spm_id) B、spm-url,当前页面的来源位置的SPM编码(url_spm_id) C、spm-pre,来源页面的来源位置的SPM编码(pre_spm_id) D、由a.b.c.d四段构成,各分段分别代表 a:站点/业务 b:页面 c:页面区块 d:区块内点 其中a,b位是必须具备的,且均需在SPM申请中心,如a1.b1.c1.d1 (2)SCM,全称超级内容模型,Super Content Model (3)Quick Tracking,快速追踪(全域采集与增长分析),阿里云推出的(基于aplus.js的)企业级流量统计分析产品 (4)黄金令箭,用户按照约定的格式向日志服务器发送请求 A、系统自动采集的页面浏览(PV, Page View)日志 B、自定义日志(时间内容自定义),通过调用黄金令箭日志接口主动上报日志 (5)aplus-auto-exp,曝光时,aplus自动发送黄金令箭 (6)aplus-auto-clk,点击时,aplus自动发送黄金令箭 (7)自定义属性,data-spm、data-spmA、data-spmB (8)自动点击,spmC、spmD 3、aplus.js采集内容包括 (1)ID,cookieID、淘宝会员数字id(不一定需要登录)、nickname(若已登录) (2)URL,当前页URL、来源页URL (3)SPM,当前页SPM、来源页点击位置SPM、来源页的来源页点击位置SPM编码(前提为,已部署了SPM) (4)信息,标题、浏览器名称版本、屏幕分辨率、aplus版本 (5)反作弊验证码 附、交互行为使用「黄金令箭」统计 4、aplus.js自动埋点,当某个元素出现在可视区域,或者点击某个元素的时候,aplus会自动发送黄金令箭 来源,https://blog.csdn.net/qq_38397338/article/details/125246947 (1)html埋点, A、标识站点ID,<meta name="data-spm" content="申请的A位编码"> B、标识页面ID,<body data-spm="注册的B位编码"></body> C、标识容器ID,<div data-spm="自定义或注册的C位编码"></div> D、标识链接ID,<a data-spm="d开头的自定义编码串" href=""></a> E、html埋点,<meta name="aplus-auto-exp" content='[{"logkey":"/abc1","tag":"div","filter":"data-name","props":["name","age","address"]}]'> 当带有data-name属性的div标签被曝光时,自动采集这个标签的曝光信息,及当前元素的"name",“age”,"address"这几个属性 (2)js埋点 A、示例 handleClick(clkType) { var q = (window.goldlog_queue || (window.goldlog_queue = [])); q.push({ action: 'aplus.record', //发送日志请求。放在这里调用,是为了避免aplus未完成初始化 arguments: [ '/aplus.99.3',//(埋点方案中的事件编码) 'CLK', 'clickType='+clkType, 'POST' ] }); q.push({ action: 'aplus.setMetaInfo', //识别需要曝光的元素 arguments: ['aplus-auto-exp', [ { //aplus自动曝光,发送黄金令箭 cssSelector: '.auto-exp-component', //需要曝光的元素class positionSelector: '.parent',//如果页面模块是元素内滚动(某个区块内有滚动条)则需要增加positionSelector辅助定位曝光元素 logkey: 'test_event_id', //事件管理中的事件id props: ['data-itemid'], //你要曝光的元素身上自定义属性 }, ... ], ], }); q.push({ action: 'aplus.setMetaInfo', arguments: ['aplus-auto-exp', JSON.stringify([ { "logkey": "/abc1", "tag": "div", "filter": "data-name", "props": ["name", "age", "address"] } ]) ] }); q.push({ 'action':'aplus.sendPV', 'arguments':[{ is_auto: false }, { page_title: "首页", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传) page_name: "yourCurrentPageName", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传) //如果您设置了duration参数(单位须为毫秒),QuickTracking会做为分析时的「事件属性-时长(s)」处理 duration: 1111111, //自定义事件属性 x: 111, y: 222 }] }); } <button onClick={this.handleClick('one')}>HJLJ ONE</button> B、参数说明,goldlog.record(logkey, gmkey, gokey, req_method) logkey{String},即完整的令箭编码,如上例中的 “/a1.jingyao.clicktest” gmkey{String},关键业务类型,目前的约定的元值有五个, 点击类操作"CLK"、 曝光类事件"EXP"、 滑屏类事件"SLD"、 其它事件"OTHER"(特指除点击和曝光事件外的其他自定义事件)、 也可以为空值,但不建议留空 gokey{String},附加的自定义kv对,如本例中clicktype=one req_method{String},可选值有’GET’(默认)、‘POST’ 如果入参为POST,则令箭请求会优先navigator.sendBeacon post的形式发出,可以保证页面跳转的时候不会断 如果当前浏览器不支持sendBeacon则降级为get img的形式发出 5、Quick Tracking的基本概念,全域采集与增长分析-产品概述 来源,https://help.aliyun.com/document_detail/250932.html (1)行为采集 A、系统事件(APP有,小程序有,Web无),应用启动($$_app_start)、应用退出($$_app_end)、分享($$_share) B、页面事件,采集页面浏览行为的事件,包含页面事件标识码和页面编码 C、自定义事件, D、属性,包含全局属性和事件属性 (2)用户标识,包含设备和用户 6、Web可视化分析使用文档-集成,全域采集与增长分析-操作指南-采集管理-可视化埋点 附、相关概念 A、可视化分析,利用图形、图像、动画等直观方式来展现数据的分析方法 B、可视化埋点,用于分析用户在应用程序或网站上的行为和交互的技术 来源,https://help.aliyun.com/document_detail/281212.html (1)第一步:确认SDK支持投屏 注、支持投屏,意味着一个设备能够将它的画面复制并显示在另一个设备上 A、用户将发射端SDK集成到自己的APP或软件应用中 B、配套投屏接收端设备 (2)第二步:确认SDK已经集成 A、首先,将以下脚本放在head标签内 <script> (function(w, d, s, q) { w[q] = w[q] || []; var f = d.getElementsByTagName(s)[0],j = d.createElement(s); j.async = true; j.id = 'beacon-aplus'; j.src = '<sdk地址>'; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'aplus_queue'); </script> B、其次,配置必要的meta <html> <head> <meta name="appKey" content="<QuickA+申请应用时颁发的appKey>"> <meta name="aplus-rhost-v" content="<日志收数域名>"> <meta name="aplus-vt-cfg-url" content="<已发布的配置地址>"> </head> </html> C、说明 appKey:QuickA+申请应用时颁发的appKey aplus-rhost-v:日志收数域名 aplus-vt-cfg-url:已发布的配置地址 (3)第三步:配置SPM 注、必须使用自动点击、自动曝光上报数据 A、配置页面级别spm(spmB) 第一种:通过给body标签添加自定义属性data-pagename <body data-pagename=${yourPageName}></body> 第二种:手动调用API设置spmB aplus_queue.push({ action: 'aplus.setPageName', arguments: [${yourPageName}] }); B、自动点击配置spm(spmC、spmD) 注、通过每一自动点击配置项的spmC和spmD属性来配置spm aplus_queue.push({ action:'aplus.setMetaInfo', arguments:['aplus-auto-clk',[{ cssSelector:'.header', logkey:'banner-clk', spmC:"header", spmD:"banner" },{ cssSelector:'.component-category-industry', logkey:'category-clk', props:['categorytype','title'], spmC:"component-category", spmD:"industry" },{ cssSelector:'.component-category-common', logkey:'category-clk', props:['categorytype','title'], spmC:"component-category", spmD:"business" }]] }); C、自动曝光配置spm(spmC、spmD) 注、通过每一自动曝光配置项的spmC和spmD属性来配置spm aplus_queue.push({ action:'aplus.setMetaInfo', arguments:['aplus-auto-exp',[{ cssSelector:'.header', logkey:'banner-exp', spmC:"header", spmD:"banner" },{ cssSelector:'.component-category-industry', logkey:'category-exp', props:['categorytype','title'], spmC:"component-category", spmD:"industry" },{ cssSelector:'.component-category-common', logkey:'category-exp', props:['categorytype','title'], spmC:"component-category", spmD:"business" }]] }); D、自定义属性配置spm(spmC、spmD) <body data-pagename=${yourPageName}> <div data-spmc="c111"> <a href="链接1" data-spmd="d1" /> <a href="链接2" data-spmd="d2" /> </div> <div data-spmc="c222"> <a href="链接3" data-spmd="d1" /> <a href="链接4" data-spmd="d2" /> </div> </body> data-pagename:spmB data-spmc:spmC data-spmd:spmD 7、引入&初始化SDK,全域采集与增长分析-开发参考-SDK参考-Web SDK 来源,https://help.aliyun.com/document_detail/473456.html (1)参数准备 A、appkey:在应用列表中获取 B、收数域名:在“管理控制台-采集信息”模块中获取 C、SDK链接:在“管理控制台-采集信息”模块中获取 (2)SDK引入&初始化 A、当您得到集成SDK代码地址以后,在页面head标签内加入集成代码,确保aplus_queue不被污染 (function(w, d, s, q) { w[q] = w[q] || []; var f = d.getElementsByTagName(s)[0], j=d.createElement(s); j.async = true; j.id = 'beacon-aplus'; j.src = 'SDK链接'; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'aplus_queue'); B、设置域名和appkey,以下代码紧跟SDK引入代码 //集成应用的appKey aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['appKey', '您的appkey'] }); //如果是私有云部署还需要在上面那段JS后面紧接着添加日志域名埋点 //通常私有云日志服务端域名类似于:xxx-web-api.xxx.com.cn, 具体域名在“管理控制台-采集信息”模块中获取 //2.x版本SDK设置收数域名 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['trackDomain', '您的收数域名'] }); //1.x版本SDK设置收数域名 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['aplus-rhost-v', '您的收数域名'] }); 8、基础功能,全域采集与增长分析-开发参考-SDK参考-Web SDK 来源,https://help.aliyun.com/document_detail/602428.html (1)原理 A、SDK 提供一种指令形态的埋点调用方式,您通过对 aplus 环境变量的指令队列 aplus_queue发送指令, B、由 aplus 环境变量来执行指令,进而完成您的需求,: C、指令格式如下 aplus_queue.push({ 'action': "$APIName", 'arguments': [$arguments]//arguments为指定API的入参, }) (2)action 参数代表发送指令的 API 名称,其入参为一个字符串,取值为枚举值,可用的枚举值如下 A、setMetaInfo:覆盖SDK的已有默认设置 B、appendMetaInfo: 追加SDK的默认配置 C、getMetaInfo:获取SDK的当前配置 D、record:发送事件日志 E、sendPV:发送页面日志 (3)arguments参数,为action中指定API的入参,格式是一个数组,数组内的元素顺序与API定义的入参顺序一致 (4)示例 A、变更SDK的默认设置 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: [metaName, metaValue] }); B、获取SDK的当前配置 aplus.getMetaInfo(metaName); C、发送事件日志 aplus_queue.push({ action: 'aplus.record', arguments: [ trackerEventCode, //(埋点方案中的事件编码) eventType, eventParams ] }); D、发送页面日志 aplus_queue.push({ action: 'aplus.sendPV', arguments: [pageEventConfig, userData] }); (5)日志打印 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['DEBUG', true] }); (6)应用基础信息配置 在SDK引入部分,可以修改或者追加一些默认设置 //集成应用的appKey aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['appKey', 'xxxxxxx'] }) aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['aplus-rhost-v', 'quickaplus-Web-api.xxx.com.cn'] }); //开启调试模式 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['DEBUG', true] }); 9、埋点API,全域采集与增长分析-开发参考-SDK参考-Web SDK 来源,https://help.aliyun.com/document_detail/602417.html (1)设备ID设置 A、自动生成:默认逻辑,网站的设备ID只有浏览器发生变化或用户主动清除cookie和缓存时,设备ID会发生改变 B、手动上传:上传方式为赋值给"_dev_id",上传的长度要在24-36字符 C、示例 a、如采集用户ID是异步行为,需要先阻止SDK上报,设置BLOCK埋点 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_hold', 'BLOCK'] }); b、设置_dev_id aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_dev_id', '自定义设备ID'] }); c、因为采集用户ID是异步行为,故需要先设置BLOCK,再设置START 设置_hold=START后,事先被block住的日志会携带上用户信息逐条发出 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_hold', 'START'] }); (2)账号ID设置 A、在用户登录时,以及登录态进入H5时,都需要设置账号ID B、因为设置后的每一条日志都将携带账号ID,但退出H5再进入后触发的事件不会携带账号ID C、所以需要在用户登录时,以及登录态进入H5时设置账号ID D、示例 a、用户登录时,获取到用户登录账号信息 or 用户已登录,通过cookie或者localstorage获取用户登录账号 function demoLogin() { /*************************如果同步场景***********************************/ aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_user_id', '用户的账号id'] }); /******************如果是异步场景,并且日志必须依赖用户账号***********************/ //先通过设置_hold=BLOCK阻塞采集上报 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_hold', 'BLOCK'] }); ... function callback() { //获取异步回调结果中的用户账号id aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_user_id', '用户的账号id'] }); //再通过设置_hold=START允许采集上报 aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_hold', 'START'] }); }; ... }; b、用户登出时,重置用户账号id function demoLogOff() { aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_user_id', ''] }); }; (3)设备ID和账号ID获取 A、设备ID的获取 a、SDK自动生成的设备ID,获取方式如下: 在当前域名的cookie下存储名为cna的字段,可以通过解析document.cookie获取 b、通过_dev_id方式自定义上传的设备ID,获取方式如下: 开发者通过setMetaInfo设置_dev_id可以自定义设备ID,可通过aplus.getMetaInfo('_dev_id')读取 B、账号ID的获取 a、开发者通过setMetaInfo设置_user_id可以自定义用户账号ID,可通过aplus.getMetaInfo('_user_id')获取 (4)设置用户属性 A、通过预制事件编码 $$_user_profile 上报用户属性,事件类型为其他事件 B、在上报用户属性之前,需要先设置_user_id上报用户账号 C、示例 aplus_queue.push({ 'action':'aplus.record', 'arguments':[ '$$_user_profile(埋点方案中的事件编码)', 'OTHER', //特指除点击和曝光事件外的其他自定义事件 { //此外内容不可改变 name: 'sss', //用户属性1 gender: 'male', //用户属性2 class: '3', //用户属性3 } ] }); 10、常见场景与埋点建议,全域采集与增长分析-开发参考-SDK参考-Web SDK 来源,https://help.aliyun.com/document_detail/603464.html (1)页面跳转前的事件发送 A、当用户在网页「点击href属性www.xxxx.com的a标签」时,触发的点击事件可能会因为页面立刻跳转而未发送出去, B、若希望该场景下尽量保证数据的发送,可以进行页面延迟跳转, C、事例代码如下 //点击链接 function targetLinkCLK(url) { // 延迟页面跳转,给SDK预留发数时间 setTimeout(function(){ window.location.href = url; }, 500); aplus_queue.push({ action: 'aplus.record', arguments: [ 'track_alink_clk', //(埋点方案中的事件编码) 'CLK', { param1: xxxx, param2: xxxx } ] }); } 二十、aplus.js源码阅读 1、外框 ! function(e) { //e,总参数 function t(a) { //公函数, if (o[a]) return o[a].exports; var n = o[a] = { exports: {}, id: a, loaded: !1 }; return e[a].call(n.exports, n, n.exports, t), n.loaded = !0, n.exports //公函数执行,参数的某项执行 } var o = {}; //总数据 return t.m = e, t.c = o, t.p = "", t(0) //公函数执行 }([function(e, t, o) { //对象,属性,公函数。公函数执行,参数的某项执行 "use strict"; ! function() { var e = window.goldlog || (window.goldlog = {}); e._aplus_plugin_cctv || ( e._aplus_plugin_cctv = { status: "complete" }, o(1).run() ) }() }, function(e, t, o) { "use strict"; function a() { var e = l.getCookie("userSeqId"); if (e) { var t = document.getElementById("tb-beacon-aplus") || document.getElementById("beacon-aplus"); if (t) { var o = t.getAttribute("exparams"), a = "uidaplus=" + e; o = o ? o.replace(/&aplus&/, "&" + a + "&aplus&") : a + "&aplus&sidx=aplusSidex", t.setAttribute("exparams", o) } } return e } function n() { var e = {}; try { var t = goldlog.getMetaInfo("aplus-rhost-g-map"); "string" == typeof t ? e = JSON.parse(t) : "object" == typeof t && (e = t) } catch (t) { e = {} } return e } function r(e, t) { var o = n(); return o && o[t] ? "//" + o[t] + t : e } var s = o(2), l = o(3); t.run = function() {} }]); 2、功能扩展-单页面重新获取信息 来源,https://help.aliyun.com/document_detail/473456.html?spm=a2c4g.602417.0.0.b24b256fiPDTu3 (1)当您得到集成SDK代码-地址-以后,在页面head标签内加入集成代码,确保aplus_queue不被污染 (function(w, d, s, q) { w[q] =w[q] || []; var f=d.getElementsByTagName(s)[0],j=d.createElement(s); j.async=true; j.id='beacon-aplus'; j.src='SDK链接'; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'aplus_queue'); //扩充功能-应该-写在这里 (2)扩充功能 function pushData(){ // var aaa = window.goldlog.getMetaInfo("aplus-auto-exp") window.goldlog_queue.push({ action: 'aplus.sendPV',//发送页面日志 arguments:[ { is_auto: true }, { page_title: "首页", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传) page_name: "yourCurrentPageName", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传) //如果您设置了duration参数(单位须为毫秒),QuickTracking会做为分析时的「事件属性-时长(s)」处理 duration: 1000000000, //自定义事件属性 x: 111, y: 222 } ] }); } function isFunction(myFunction){ return Object.prototype.toString.call(myFunction) === "[object Function]" } function addEveryPageEvent() { var historyPushState = window.history.pushState; var historyReplaceState = window.history.replaceState; if (isFunction(window.history.pushState)) { window.history.pushState = function() { console.log( 'pushState' ); historyPushState.apply(window.history, arguments); pushData() }; } if (isFunction(window.history.replaceState)) { window.history.replaceState = function() { console.log( 'replaceState' ); historyReplaceState.apply(window.history, arguments); pushData() }; } if (window.addEventListener) { window.addEventListener('popstate', function(){ console.log( 'popstate' ); pushData() }, false); } } addEveryPageEvent() (3)扩充功能不应该写在这里 e.init = function() { i.initLoad.init_watchGoldlogQueue("metaQueue"), n(90)(function() { }) //扩充功能-不应该-写在这里 } 3、使用说明 来源,https://help.aliyun.com/document_detail/473456.html?spm=a2c4g.602417.0.0.b24b256fiPDTu3 (1)在QuickTracking后台,为每一个Web应用生成了专属的集成代码,可以根据产品内的引导进行集成 (2)参数准备 A、appkey,在应用列表中获取 B、收数域名,在“管理控制台-采集信息”模块中获取 C、SDK链接,在“管理控制台-采集信息”模块中获取