<head>
<!-- 声明文档使用的字符编码 -->
<meta charset="utf-8">

<!-- 声明文档的兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"><!-- 指示ie以目前可用的最高模式显示内容 -->

<!-- 定义对页面的描述 -->
<meta name="description" content="官网首页模板">

<!-- 定义页面的最新版本 -->
<meta name="revised" content="Frank,2017/7/15"><!-- SEO优化 -->

<!-- 定义针对搜索引擎的关键词 -->
<meta name="keywords" content="官网,模板,首页,...">

<!-- 定义网页的作者 -->
<meta name="author" content="某某某">

<!-- 定义网页搜索引擎索引方式 ,常见的几种取值none,noindex,nofollow,all,index和follow-->
<meta name="robots" content="index,follow">

<!-- 为移动设备添加viewport -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

<!-- IOS设备 -->
<!-- 添加到主屏后的标题(ios6开始) -->
<meta name="apple-mobile-web-app-title" content="标题">

<!-- 设置状态栏的背景颜色(前提) -->
<meta name="apple-mobile-web-app-title" content="yes">
<!-- 设置状态栏背景色content参数释义:default 默认值,网页内容从状态栏底部开始
black 状态栏背景是黑色,网页内容从状态栏底部开始
black-translucent 状态栏背景是黑色半透明,网页内容充满整个屏幕,顶部会被状态栏遮挡 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

<!-- 是否启用webapp全屏模式 -->
<meta name="apple-mobile-web-app-capable" content="yes">

<!-- window8 -->
<!-- window8磁贴颜色 -->
<meta name="msapplication-TileColor" content="#000">

<!-- window8磁贴图标 -->
<meta name="msapplication-TileImage" content="icon.png">

<!-- 禁止数字自动识别为电话号码 -->
<meta name="format-detection" content="telephone=no">

<!-- 不让Android识别邮箱 -->
<meta name="format-detection" content="email=no">

<!-- 每8秒刷新一次页面 -->
<meta http-equiv="refresh" content="n">

<title>我的官网模板</title>

<!-- 引入网站小图标 -->
<!--shortcut icon,特指浏览器中地址栏左侧显示的图标,一般大小为16x16,后缀名为.icon -->
<!-- icon,指的是图标,格式可为PNG\GIF\JPEG,尺寸一般为16x16、24x24、36x36等。-->
<link rel="shortcut icon" href="logo.png" type="image/x-icon">
<link rel="icon" href="logo.png" type="image/x-icon">

</head>

设置中找到 file watchers 添加

填写配置

program 填写 sed

arguments填写 -i "" s/"\ rpx"/rpx/g $FilePath$

Output paths to refresh 填写 $FilePath$

然后添加保存就完事了

闭包,是JavaScript 一个重要的概念,一个抽象的概念,特别是ECMAscript规范的定义,如果没有实战经验,很难从定义去理解它。

简陋的实现


function A() {
function B() {
console.log('Hi Chris ~');
}

return B
}

let C = A(); // 保存A方法执行的结果,也就是B函数体
C(); // 这里可以看做是B该函数体执行

这是最简单的闭包

初步的认识后,我们简单分析一下它和普通函数有什么区别,上面代码翻译成人话如下:

函数A的内部函数B ,被函数A外的一个变量C调用。

从而可以得到整个闭包的定义

当一个内部函数( 函数中定义的函数 )被其外部函数之外的变量调用时,就形成了一个闭包。

用途? 应用场景?


了解闭包之前,先了解一下JavaScript中的GC机制 ( 垃圾回收机制 ) :

在JavaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象会一直保存在内存中。

上面的🌰中,B定义在A中,因此B依赖于A ,而外部变量C 又调用了B,所以A间接的被C调用。

也就是说,A 不会被GC回收,会一直保存在内存中,为了证明我们的推理,对上面的例子进行简陋的改进

function A() {
let count = 10;
function B() {
count ++;
console.log(count);
}

return B
}

let C = A(); // 保存A方法执行的结果,也就是B函数体
C(); // 11
C(); // 12
C(); // 13 A没有被释放,一直保存着 count 每次运算后的结果

count 是函数A中的一个变量,它的值在函数B中被改变,函数B每执行一次,count 的值就在原来的基础上累加, 因此A中的count变量会一直保存在内存中。

当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中,但又不会污染全局变量的时候,就可以使用闭包来定义这个模块。

似曾相识的写法


有看过Jquery源码的同志都应该觉得好眼熟有没有

(function (document) {
let viewport;
let obj = {
init: function (id) {
viewport = document.querySelector('#' + id);
},
addChild: function (child) {
viewport.appendChild(child);
},
removeChild: function (child) {
viewport.removeChild(child);
}
}
window.jView = obj;
})(document);

这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。

功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行,需要重点理解的是这种写法是如何实现闭包功能的。

可以将上面的代码拆分成两部分:**(function(){})** 和 ()

第1个**()** 是一个表达式,而这个表达式本身是一个匿名函数,所以在这个表达式后面加 () 就表示执行这个匿名函数。

因此这段代码执行执行过程可以分解如下:

let f = function (document) {
let viewport;
let obj = {
init: function (id) {
viewport = document.querySelector('#' + id);
},
addChild: function (child) {
viewport.appendChild(child);
},
removeChild: function (child) {
viewport.removeChild(child);
}
}
window.jView = obj;
};
f(document);

在这段代码中似乎看到了闭包的影子,但 f 中没有任何返回值,似乎不具备闭包的条件,注意这句代码:

`window.jView = obj;`

obj 是在函数 f 中定义的一个对象,这个对象中定义了一系列方法, 执行window.jView = obj 就是在 window 全局对象定义了一个变量 jView,并将这个变量指向 obj 对象,即全局变量 jView 引用了 obj . 而 obj 对象中的函数又引用了函数 f 中的变量 viewport ,因此函数 f 中的 viewport 不会被 GC 回收,viewport 会一直保存到内存中,所以这种写法满足了闭包的条件。

总结


这是对闭包最简单的理解,当然闭包还有其更深层次的理解,这个就涉及的多了,你需要了解JS的执行环境(execution context)、活动对象(activation object)以及作用域(scope)和作用域链(scope chain)的运行机制。但作为初学者,暂时不必了解这些,有了简单的理解之后,一定要在实际项目中用起来,等你用的多了,对于闭包,你自然会有更深层次的理解!

节流


什么是节流? 节流的目的?

节流指尽可能减少不必要的开支 ,消耗更少的资源办更多的事

节流的理解

原理是通过判断是否到达一定时间来触发函数

为什么要节流?

在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如 resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。

简陋的实现

<script>
let body = document.body;
let start = 0;
let key = false;
body.onmousemove = function () {
throttle1(300, function () {
console.log(1);
})
};
// 方法1 通过触发事件的时间间隔,判断节流处理
function throttle1(delay, fn) {
if (!start) start = Date.now();
let currentTime = Date.now();
if (currentTime - start > delay) {
fn(); start = 0;
}
}
// 方法2 通过上锁的方式做节流处理(如果代码执行中,则终止操作)
function throttle2(delay, fn) {
if (key) return false;
key = true; setTimeout(function () {
fn();
key = false;
}, delay)
}
</script>

防抖


什么是防抖?

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖的目的

在一定时间内的几次操作合并为一次操作进行。

防抖的理解

原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。

简陋的实现

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>防抖 - debounce</title>
</head>
<body>
<input type="text" id="ipt">
</body>
</html>
<script>
let ipt = document.querySelector('#ipt');
let timer;

ipt.onkeyup = function () {
debounce(2000, function () {
console.log(1);
})
};

/**
* 防抖封装函数 - 应用场景(联想查询)
*/
function debounce(delay, fn) {
if (timer) clearTimeout(timer);
timer = setTimeout(fn, delay)
}
</script>

代码封装

/**
* 防抖函数
* @param method 事件触发的操作
* @param delay 多少毫秒内连续触发事件,不会执行
* @returns {Function}
*/
export const debounce = (method, delay) => {
let timer = null;
return function () {
let self = this,
args = arguments;
timer && clearTimeout(timer);
timer = setTimeout(function () {
method.apply(self, args);
}, delay);
}
}


/**
* 节流函数
* @param method 事件触发的操作
* @param mustRunDelay 间隔多少毫秒需要触发一次事件
*/
export const throttle = (method, mustRunDelay) => {
let timer,
args = arguments,
start;
return function loop() {
let self = this;
let now = Date.now();
if (!start) {
start = now;
}
if (timer) {
clearTimeout(timer);
}
if (now - start >= mustRunDelay) {
method.apply(self, args);
start = now;
} else {
timer = setTimeout(function () {
loop.apply(self, args);
}, 100);
}
}
}

app.wxss 全局样式修改

隐藏scroll-view滚动条

/*隐藏scroll-view滚动条*/
::-webkit-scrollbar{
width: 0;
height: 0;
color: transparent;
}

去除Button默认边框

/*去除Button默认边框*/
button::after {
border: none;
}

去除点击 navigator 链接样式

/*去除点击 navigator 链接样式*/
.navigator-hover {
color: none;
background: none;
}

刷新页面图片加载时变形问题

<image 
class="img"
mode="widthFix"
src="你的图片地址">
</image>
.class {
height: auto;
}

setData 设置数组中的某一项值

data: {
isChecked: [true, true, true, true, true, true, true, true, true, true]
}

click : function (e) {
let index = e.target.index; //根据点击不同的view获取对应的id值
let str = `isChecked[${index}]`; //重点在这里,组合出一个字符串
this.setData({
[str]: false //用中括号把str括起来即可
})
}

携带参数放回上一页

onUnload() {
let pages = getCurrentPages(); // 当前页面栈
let prevPage = pages[pages.length - 2]; // 上个页面栈
console.log(prevPage);
// 把数据直接设置到上个页面的data里面,返回后,在onShow里面可以直接通过this.data 获取到
prevPage.setData({
book_id: this.data.book_id
});
// wx.navigateBack({});
}

//tsconfig.json指定了用来编译这个项目的根文件和编译选项
{
//compilerOptions:编译选项,可以被忽略,这时编译器会使用默认值
"compilerOptions": {

//允许从没有设置默认导出的模块中默认导入。这并不影响代码的显示,仅为了类型检查。
"allowSyntheticDefaultImports": true,

//解析非相对模块名的基准目录
"baseUrl": "./src",

//给源码里的装饰器声明加上设计类型元数据
"emitDecoratorMetadata": true,

//启用实验性的ES装饰器
"experimentalDecorators": true,

//指定生成哪个模块系统代码
"module": "commonjs",

//决定如何处理模块。或者是"Node"对于Node.js/io.js,或者是"Classic"(默认)
"moduleResolution": "node",

//不再输出文件中生成用户自定义的帮助函数代码,如__extends。
"noEmitHelpers": true,

//在表达式和声明上有隐含的any类型时报错
"noImplicitAny": false,

//用于debug ,生成相应的.map文件
"sourceMap": true,

//在严格的null检查模式下,null和undefined值不包含在任何类型里,只允许用它们自己和any来赋值(有个例外,undefined可以赋值到void)。
"strictNullChecks": false,

//目标代码类型
"target": "es5",

"paths": {
//模块名到基于baseUrl的路径映射的列表
},

"lib": [
//编译过程中需要引入的库文件的列表
"dom",
"es6"
],

"types": [
//要包含的类型声明文件名列表;如果指定了types,只有被列出来的包才会被包含进来
"hammerjs",
"node",
"source-map",
"uglify-js",
"webpack"
]
},

"exclude": [
//如果"files"和"include"都没有被指定,编译器默认包含当前目录和子目录下所有的TypeScript文件(.ts, .d.ts 和 .tsx),排除在"exclude"里指定的文件。
"node_modules",
"dist"
],

//Typescript加载选项
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},

"compileOnSave": false,

"buildOnSave": false,

"atom": {
"rewriteTsconfig": false
}
}

原因: 前端传入的数据格式和后台接口接收数据类型不一致

1. URLSearchParams (不推荐)

URLSearchParams 对URL上的字符串数据进行类型处理

此方法不兼容IE 和 edge 浏览器 链接

代码展示

let paramsObj = new URLSearchParams();
paramsObj.append('openid', 'oSrdV47XurWKLdhjheQ_9U1UKrWQ'); // key,value
axios({
method: 'post',
url: '/api_prod/booklist/get_topic_list',
data: paramsObj
}).then(res => {
// Coding
})

2. qs模块 stringify方法

字符串解析和序列化字符串的库

安装

npm install qs --save

示例代码

import axios from 'axios'
import qs from 'qs'

created() {
axios({
method: 'post',
url: '/api_prod/booklist/get_topic_list',
data: qs.stringify({
key: 'value'
}),
}).then(res => {
// Coding
})
}

3. 和后台协商改变接收参数方式

上面这些情况一般都是由于前后端数据类型交互格式不一致所导致的

例如后端使用的是 application/x-www-form-urlencoded格式
数据格式为:id=1&name=chris

前端发送的数据是 application/json 格式
数据格式为:{id:1,name:'chris'}

就会纯在上面所说的情况,需要用一些方法去处理和转译

然儿最好的办法还是直接从文档上就统一好这些规范,避免在开发时出现不必要的联调,导致降低效率

打开设置

进入 WebStorm preferences -> Language & Framework -> JavaScript -> Webpack

选择文件

项目目录\node_modules@vue\cli-service\webpack.config.js

项目根目录新建vue.config.js

touch vue.config.js

设置跨域

module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://www.boomore.cn',
changeOrigin: true, // 是否开启跨域
pathRewiter: {
'^/api_prod': ''
}
}
}
}
};

调用API

axios({
method: 'post',
url: '/api_prod/booklist/get_topic_list',
data: qs.stringify({
key: value,
}),
}).then(res => {
// Coding
})

安装NPM模块

npm install nodemailer --save

简陋的实现

const NodeMailer = require('nodemailer');     //@modules  npm install nodemailer

// 开启一个 SMTP 连接池
let transporter = NodeMailer.createTransport({
service: 'qq',
post: 465, // SMTP 端口
secureConnection: true, // SSL
auth: {
user: '[email protected]',
pass: 'xxxxxxxxxxxxx', // SMTP 授权码, 需要在邮箱设置中开启SMTP服务
}
});

// 设置邮件内容
let mailOptions = {
from: '[email protected]', // 发件地址
to: '[email protected]', // 收件地址
subject: 'Title', // 标题
text: 'Hello ChrisChen', // text | html 只能选择一种作为内容
// html: '<h1>Hello ChrisChen</h1>'
};

// 发送邮件
transporter.sendMail(mailOptions, function (err, info) {
if (err) throw err;
console.log(info);
transporter.close(); // 关闭连接池
});

0%