[Vue 牛刀小试]:第十七章 - 优化 Vue CLI 3 构建的前端项目模板(1)- 基础项目模板介绍
一、前言
在上一章中,我们开始通过 Vue CLI 去搭建属于自己的前端 Vue 项目模板,就像我们 .NET 程序员在使用 asp.net core 时一样,我们更多的会在框架基础上按照自己的开发习惯进行调整。因此在后面几章的学习中,我将会在整个项目基础上,按照自己的需求进行修改设定。
PS:因为毕竟自己还是传统意义上的后端开发,所以这里最终搭建完成的前端项目模板,其实是按照 PanJiaChen 开源的 vue-admin-template 模板进行修改仿写,所以你可以把这个系列后续的文章当成是对于 vue-admin-template 模板的使用资料补充。
系列目录地址:https://www.cnblogs.com/danvic712/p/9549100.html
仓储地址:https://github.com/Lanesra712/ingos-web
二、干货合集
在调整项目模板前,我们首先还是先来了解下我们通过 Vue CLI 3 所搭建的这个基于 Element UI 的项目模板,整个模板的文件结构及相关解释说明如下所示。
|-- ingos.web
|-- node_modules // 项目所引用的前端组件包
|-- public // 项目发布后打包后的目录地址
|-- favicon.ico
|-- index.html
|-- src // 项目源文件路径
|-- assets // 静态存放路径
|-- logo.png
|-- components // 项目中定义的组件存放路径
|-- HelloWorld.vue
|-- plugins // 项目中引用到的第三方 Vue CLI 插件所在路径
|-- element.js
|-- views // 项目中视图所在路径
|-- About.vue
|-- Home.vue
|-- App.vue // 项目的主组件,项目中的页面都是在此进行路由切换
|-- main.js // 主入口文件,初始化 Vue 实例并使用加载项目中需要使用的插件
|-- router.js // 项目中所有的路由定义
|-- store.js
|-- tests // 单元测试文件路径
|-- units // 存放单元测试用例
|-- .eslintrc.js
|-- example.spec.js
|-- .browserslistrc // 指定项目的目标浏览器的范围
|-- .editorconfig // 针对不同的编辑器和 IDE 之间对于代码风格的设定
|-- .eslintrc.js // eslint 的配置文件
|-- .gitignore // git 忽略添加的文件
|-- babel.config.js // Babel 规则配置文件
|-- package-lock.json // 记录安装包的具体版本号
|-- package.json // 项目加载的组件包
|-- postcss.config.js // 针对 postcss 的配置
|-- README.md // 项目 readme 文件
1、原始代码结构了解
1.1、webpack 的基础概念
因为很多这个系列的读者童鞋可能和我一样,就是传统意义上的后端开发,在之前完全没有接触过这种进行前端工程化的开发模式,所以这里我会针对项目的基础模板进行一个简单的说明,如果存在不对的地方,欢迎在评论区指出。
整个项目虽然是通过 Vue CLI 进行搭建的,但是因为 Vue CLI 其实也是基于 webpack 进行构建的,所以这个项目的本质上其实是个 vue.js + webpack 项目,因此在后面的使用中会涉及到很多 webpack 的相关知识点。作为目前最主流的前端构建工具,webpack 本身的知识点会很多,所以这里只是对使用到的相关知识点进行一个简单的概述,不会详细的介绍,后续如果有使用到的时候也会进行补充。
在前端项目开发中,我们可能会引入很多的 css、js、fonts、imgs 或是其它的静态文件到页面中,当一个页面引入了很多的静态文件时,为了加载这些静态资源,网页会发起很多个二次请求,从而导致页面的加载变慢。同时,我们在使用前端框架时,经常会存在很多的依赖关系,并且由于 javascript 是一个弱类型的语言,无法在代码编写时很快速的定位到框架间的依赖问题。
webpack 则可以很好的帮我们解决这些问题。
webpack 是一个前端应用程序的静态模块打包工具,它是基于 node.js 进行开发的,所以在使用前我们需要安装 node.js。它可以帮我们实现对于网站所引用的静态资源进行打包、压缩、混淆;帮我们解决 js、css 中可能存在的依赖关系;将同类型的静态资源打包合并成一个文件,并对生成的代码进行混淆,以增加线上代码的安全性。
1.2、Vue 的单文件组件
当我们简单了解了项目的框架基础后,我们可以从一些之前我们没有接触过的文件去入手,去逐渐了解整个前端项目。在项目文件中我们可以看到一些以 .vue 结尾的文件,一个基础的 vue 文件包含了下面所示的三部分。
<template>
<p>{{ greeting }} World!</p>
</template> <script>
module.exports = {
data: function () {
return {
greeting: 'Hello'
}
}
}
</script> <style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
可以看到,与我们 .NET 程序员使用的 asp.net core mvc 框架中的视图对应的 cshtml 文件很相似,它们都是在 html 文件的基础上进行了扩展。就像在 razor 页面上(.cshtml)我们可以将 C# 代码嵌入到 html 代码中,并且可以得到 IDE 的代码提示一样,在 .vue 文件中,我们也可以将很多 Vue 的特性添加到 html 代码中,并可以得到很好的语法支持和代码高亮。、
在 Vue 项目中,我们一般将一个 .vue 文件作为一个组件。当然,浏览器是不能直接解析 cshtml、vue 这类特殊后缀的文件的,所以这里我们在使用 Vue CLI 创建项目时,脚手架已经帮我们安装了 Vue Loader 这个 webpack loader,从而帮助我们将 .vue 文件转换成浏览器能直接识别的 html、css、javascript 文件。
webpack 的 loader 可以在我们 import 或加载模块时进行文件的预处理,完成对引入模块的源代码进行指定格式的转换。例如像这个项目一样,我们需要把 sass 文件转变成 css 文件,所以这里我们就需要在项目中添加对于 sass-loader 的引用。
亦或者,你可能在前端项目开发中会使用到 typescript,而到项目最终运行时,我们需要将 typescript 代码转换成 javascript 代码,这时,我们就需要在项目中添加 ts-loader 从而让 webpack 自动帮我们完成从 typescript 代码到 javascript 的转换。
从项目文件夹的文件分层结构可以看出,src 是项目源代码的存放路径,路径下已经存在的 assets、components、plugins、views 这几个文件夹,我们还是按照模板的原意存放对应的文件。对于单独的文件,我们来一个个的解释具体的作用。而 tests 文件夹下存放的是单元测试的测试用例,这里就不具体解释了。
1.3、对于 App.Vue 的解释
App.vue:项目的入口组件,这里我们会对代码进行一个简单的调整,最终整个项目中编写的 Vue 组件我们都会通过 vue router 导出到这个组件上,修改后的代码如下所示。
<template>
<div id="app">
<router-view />
</div>
</template> <script>
export default {
name: 'App',
}; </script> <style>
body {
height: 100%;
font-size: 14px;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC,
Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
} * {
margin: 0;
padding: 0;
} </style>
1.4、对于 main.js 的解释
从名字上就可以看出,就像是 asp.net core 项目中的入口函数,这个 js 是整个 vue 项目的入口。我们在上面调整 App.vue 时有介绍到,最后编写好的 Vue 代码都会导出到 App.vue 文件上进行显示。在使用 Vue 时,我们是需要将构造好的 Vue 实例挂载到 dom 元素上的,从下面的代码中就可以看出,将 Vue 实例挂载到 dom 元素上的操作其实就是在这个文件中进行的。在代码中,通过引用 Element UI、Vue Router、Vuex,并将这些组件挂载到 Vue 实例上,并最终渲染到绑定的页面 dom 元素上。
import Vue from 'vue';
import App from './App.vue';
import router from './routers/router';
import store from './stores/store';
import './plugins/element'; Vue.config.productionTip = false; new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
可能你会有疑问,这里最终挂载的 dom 元素是在什么地方定义的呢?对于单页面应用来说,因为整个项目中其实只会有唯一的一个 html 页面,所以我们直接在项目中去寻找 html 页面就可以了。对于通过 Vue CLI 构建出来的项目,我们最终是将 Vue 挂载到 public 文件夹下面的 index.html 上。
1.5、对于 router.js 的解释
这个文件定义了我们整个项目的前端路由信息,因为如果将整个项目中所有的路由都配置到这一个 js 文件中,这个文件肯定会变得很庞大和不好维护,所以在后面我会添加一个 routers 文件夹去专门存放我们的前端路由信息,而原有的 router.js 文件则会起到一个导出前端路由的作用。
1.6、对于 store.js 的解释
因为在使用 Vue CLI 创建项目时,我们添加了在 Vue 中专门针对状态管理的插件 Vuex,所以这个 js 文件就是针对 Vuex 的一些配置,这里我也会在后面专门添加一个 stores 文件夹去存放项目中使用到的状态相关数据。
基础项目的 src 文件夹下就是上面提到的这些内容,接下来我会按照自己的需求去添加一些文件夹去分类存放我们在后续项目开发中可能会使用到的东西。
2、自定义调整
2.1、项目基础调整
在上面有提到,我会创建一个 routers 文件夹去专门存储项目的前端路由信息,因为作为一个需要进行前后端数据交互的项目,所以这里我创建了一个 apis 文件夹去专门存放视图组件中需要进行后端数据访问的 js 代码。
对于 apis、routers 这两个文件夹的结构,这里与存放视图的 views 下的结构大体是保持一致的,例如这里 views 下面有个 home 去存放网站首页相关的视图组件,对应的路由信息就会放在 routers 下的 home 文件夹中;同时,因为我们需要和后端进行数据交互,所以这里一些请求后端的方法会按照视图的结构存放在对应的 apis 文件夹下,分层后的代码结构如下图所示。
在这个项目中,对于项目中的视图组件我会存放到 views 文件夹下,不过对于项目网站布局的视图组件,这里我创建了一个 layouts 文件夹,去专门存放网站布局相关的视图组件文件。
同样的,对于一些我们自定义的样式文件,这里我会创建一个 styles 文件夹去存放这些样式文件;同时,对于一些基础的通用方法,这里我会放在新建的 utils 文件夹下,例如这里会存放我们基于 axios 进行封装的 http 请求方法。
在某些时候,我们的网站可能会存在一些的全局设置,例如设置侧边栏是否固定,是否显示 logo 等等,对于这些基础的设置项属性,这里我都会存放在 setting.js 文件中,一些简单的设置项如下所示。
module.exports = {
/**
* @type {string}
* @description 网站默认的 title 信息
*/
title: 'ingos web template', /**
* @type {boolean} true or false
* @description 是否固定网站的 header
*/
fixedHeader: false, /**
* @type {boolean} true or false
* @description 是否显示侧边栏的 logo
*/
sidebarLogo: false,
};
2.2、浏览器兼容性调整
在做 web 项目时,如何做到对于主流浏览器的支持,是一个庞大的工作量,如果此时还需要兼容某些上古时代的浏览器,更是灾难,嗯,说的就是你,IE6。因为需要针对一些不同的浏览器确定需要转译的 JavaScript 特性和添加对应的 CSS 浏览器前缀,如果我们还是采取手动的方式进行编写,工作量势必会很大。
而在通过 Vue CLI 构建的项目中,我们可以通过 .browserslist 文件来指明当前这个项目的目标浏览器范围,然后这个值会被通过 webpack 加载的 @babel/preset-env 和 Autoprefixer 用来确定需要那些 js 代码是需要进行转译的以及需要添加那些 CSS 浏览器前缀。
例如在这个项目中,我们声明项目的目标浏览器范围是全球使用率大于 1% 的浏览器的最新两个版本,具体的声明语法这里就不详细介绍了,你可以通过点击这个链接去了解如何指定浏览器范围(电梯直达),这里我们可以直接在工程目录下运行下面的命令来查看符合我们配置的条件而筛选出的浏览器版本范围。
npx browserslist
确定了目标浏览器后,我们就需要对使用的 js 代码和 css 代码进行一个设定,从而使支持的浏览器可以正常显示出。有些时候,我们写的某些 js 代码可能是符合 ES6 语法的,对于某些浏览器来说可能是不支持的,这时我们就可以通过 Babel 和 browserslist 进行结合,将我们使用到的 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
module.exports = {
presets: [
'@vue/app',
],
};
针对 js 代码的转换可以使用到 Babel,那么对于同样可能出现浏览器不兼容的 css 样式,这里我们就可以通过使用 postcss + Autoprefixer + browserslist 识别出需要指定支持的浏览器类型和版本,自动添加所需的带前缀的属性声明。
PostCSS 本身是一个功能比较单一的工具,它一般会和 webpack、gulp 这种前端构建工具进行结合使用,通过使用 postcss 可以支持变量和混入(mixin),增加浏览器相关的声明前缀,或是把使用将来的 CSS 规范的样式规则转译(transpile)成当前的 CSS 规范支持的格式。
#content {
display: flex;
}
而 Autoprefixer 的作用就是为 CSS 中的属性添加浏览器特定的前缀,例如上面的代码,使用了 flex 的布局模式,在经过 Autoprefixer 处理之后得到的 CSS 代码则如下所示。
#content {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
2.3、代码风格的设定
在前端项目开发中,因为对于编辑器或是使用的操作系统不会有太严格的限定,例如这里我是使用 VS Code 在 Windows 10 上进行开发的,你完全可以选择 Atom + MacOS 或是 VS Code + Ubuntu 等等组合去打开我的这个项目,然后去进行开发,而不同的编辑器和操作系统对代码的展示会有些许的差异,所以这里我们就需要对项目的代码规范进行一个设定。
针对不同编辑器的风格设定,这里使用的是 editorconfig,我们可以在 .editorconfig 文件中去设定项目规范,编辑器通过加载 editorconfig 插件之后,就可以通过读取这个配置文件,来覆盖编辑器自带的代码规范,从而达到整个项目代码风格统一的效果。
# 是否为最顶层的配置文件
root = true # 匹配文件规则
[*.{js,jsx,ts,tsx,vue}] # 代码缩进方式
indent_style = space # 缩进的空格数
indent_size = 2 # 定义换行符
end_of_line = lf # 是否去处行首行尾的空白字符
trim_trailing_whitespace = true # 文件是否以空白行结尾
insert_final_newline = true # 每行代码的最大长度
max_line_length = 100
当我们指定风格之后,需要有一个工具去辅助我们进行校验,通常我们会使用 ESLint 去对我们的代码进行检查,在我们通过 Vue CLI 去创建项目时其实已经加载了 ESLint 插件并选择了代码风格。这里我是使用的 airbnb 的 vue 代码风格和 eslint 强烈推荐的规则,你可以按照你自己的喜好去修改这块的设定。
module.exports = {
root: true, env: {
node: true,
}, extends: [
'plugin:vue/strongly-recommended',
'@vue/airbnb',
], rules: {
'no-console': 'off',
'no-debugger': 'off',
}, parserOptions: {
parser: 'babel-eslint',
},
};
2.4、环境变量的设置
正常情况下,我们的线上版本、开发版本、测试版本一些对应的请求地址是不一样的,如果我们直接把请求地址写到代码中,在后期的修改就会变得很麻烦,因此这里我们就需要对我们的项目进行环境设定。默认情况下,一个通过 Vue CLI 构建的项目会有三个环境变量,分别为 development、production、test,其实这里的 3 个环境变量就对应了我们的 package.json 中的 已经定义了的 npm 命令。
首先,我们添加 3 个环境变量文件 .env.development(开发环境)、.env.production(生产环境)、.env.staging(预发布环境) 分别对应于不同的环境的一些参数信息。因为默认是没有 staging 这个环境的,所以我们需要在 package.json 文件中去添加 staging 脚本,从而去构建出 staging 环境。
"stage": "vue-cli-service build --mode staging"
staging 环境的示例如下所示,不过,这里需要注意,因为只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端中,因此,如果我们想要在代码中获取到这里定义的变量值,我们只能以 VUE_APP_ 开头去定义环境变量中会使用到的参数。
# Node ENV 变量值
NODE_ENV = production # staging 环境标识
ENV = 'staging' # 后端 API 地址
VUE_APP_BASE_API_URL = 'http://127.0.0.1/stage-api'
2.5、package.json & package-lock.json
在项目开发中,因为我们是使用 npm 去加载前端的组件,所以会存在 package.json 这个配置文件。在这个 json 文件中定义了这个项目所需要的各种前端模块,以及项目的配置信息(比如名称、版本、许可证等等)。当我们从别处拷贝这个项目后,通过执行 npm install 命令,就会根据这个配置文件,自动下载项目中所需要引用的前端组件包。
在 package.json 这个 json 文件中存在着两个看起来很相似的节点:devDependencies 和 dependencies。devDependencies 里面的插件只适用于开发环境,不用于生产环境,而 dependencies 中引用的则是需要发布到生产环境中的。我们可以在使用 npm install 命令加载组件时通过添加 –save 修饰,表示需要将该组件添加到 dependencies 节点下面;如果你需要将引用到的 package 安装到 devDependencies 节点下,则需要使用 –save-dev 进行修饰。
因为我们在 npm 上下载的包遵循了大版本.次要版本.小版本的版本定义,而在安装插件包的时候,package.json 一般指定的是包的范围,即只对插件包的大版本进行限定。因此,当别人拷贝了你的代码,准备还原引用的包时,如果恰好在组件包更新中移除了你使用的一些特性,毫无疑问,整个项目代码就会报错。而 package-lock.json 这个文件,则可以记录实际安装的各个 package 的具体来源和版本号,此时,当别人拷贝了代码,准备还原时,就可以准确的加载到你开发时使用的组件版本。
2.6、webpack 配置
在上面我们有提到,Vue CLI 本质上也是基于 webpack 去构建的 Vue 项目,如果你有使用过 Vue CLI 2 去创建项目,你会发现原本对于 webpack 配置的 webpack.base.config.js、webpack.dev.config.js 、webpack.prod.config.js 这些配置文件已经没有了。那么这里如何去按照自己的习惯去对 webpack 进行一个调整呢?
原来,因为 Vue CLI 3 的设计思想是 0 配置,所以通过 Vue CLI 3 构建的 Vue 项目已经帮开发者已经解决绝大部分情形下的 webpack 配置,如果你有需要做一些自定义的设置,则可以去创建一个 vue.config.js 去进行自定义的配置。这里就不对配置的内容进行介绍了,我会放到下一章中去介绍这个项目对于 webpack 的一些配置。
至此,我们对于模板项目的调整也就到一段路,最终我们修改完成后的项目分层如下图所示,后续我也将在这个调整后的结构上进行搭建项目模板。
四、参考
4、手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)
[Vue 牛刀小试]:第十七章 - 优化 Vue CLI 3 构建的前端项目模板(1)- 基础项目模板介绍的更多相关文章
- Vue.js 第3章 axios&Vue过渡动画
promise 它将我们从回调地狱中解脱出来 创建和使用 var fs = require('fs') // 创建promise // reslove表示执行成功后调用的回调函数 // reject表 ...
- Vue基础项目模板
https://github.com/wanglong/vue-element-admin.git 优化 Vue CLI 3 构建的前端项目模板(1)- 基础项目模板介绍 一站式开源运维平台,分享给大 ...
- [Vue 牛刀小试]:第十五章 - 传统开发模式下的 axios 使用入门
一.前言 在没有接触 React.Angular.Vue 这类 MVVM 的前端框架之前,无法抛弃 Jquery 的重要理由,除了优秀的前端 DOM 元素操作性以外,能够非常便捷的发起 http 请求 ...
- [Vue 牛刀小试]:第十六章 - 针对传统后端开发人员的前端项目框架搭建
一.前言 在之前学习 Vue 基础知识点的文章中,我们还是采用传统的方式,通过在 html 页面上引用 vue.js 这个文件,从而将 Vue 引入到我们的项目开发中.伴随着 Node.js 的出现, ...
- [Vue 牛刀小试]:第十二章 - 使用 Vue Router 实现 Vue 中的前端路由控制
一.前言 前端路由是什么?如果你之前从事的是后端的工作,或者虽然有接触前端,但是并没有使用到单页面应用的话,这个概念对你来说还是会很陌生的.那么,为什么会在单页面应用中存在这么一个概念,以及,前端路由 ...
- [Vue 牛刀小试]:第十三章 - Vue Router 基础使用再探(命名路由、命名视图、路由传参)
一.前言 在上一章的学习中,我们简单介绍了前端路由的概念,以及如何在 Vue 中通过使用 Vue Router 来实现我们的前端路由.但是在实际使用中,我们经常会遇到路由传参.或者一个页面是由多个组件 ...
- Vue.js 学习笔记 第1章 初识Vue.js
本篇目录: 1.1 Vue.js 是什么 1.2 如何使用Vue.js 本章主要介绍与Vue.js有关的一些概念与技术,并帮助你了解它们背后相关的工作原理. 通过对本章的学习,即使从未接触过Vue.j ...
- Vue.js-01:第一章 - 一些基础概念
一.前言 Vue.React.Angular,当今前端界的三驾马车,作为传统的后端程序员,前端再也不是我们想的那种切切图就可以了,第一次接触的话,先了解了解一些基础的概念. 学习系列目录地址:http ...
- 第十七章——配置SQLServer(3)——配置“对即时负载的优化”
原文:第十七章--配置SQLServer(3)--配置"对即时负载的优化" 前言: 在第一次执行查询或者存储过程时,会创建执行计划并存储在SQLServer的过程缓存内存中.在很多 ...
随机推荐
- 搭建Springboot网站有感
最近心血来潮,搭建了个人网站,一方面想学习下新的知识,另一方面也想有个作品,在这分享下自己的体会,先不说知识点. 建站容易吗,因人而异,而我在完成这个最最简单的工作时起码经历了3个阶段不同的心理变化, ...
- UVA - 1152 --- 4 Values whose Sum is 0(二分)
问题分析 首先枚举a和b, 把所有a+b记录下来放在一个有序数组,然后枚举c和d, 在有序数组中查一查-c-d共有多少个.注意这里不可以直接用二分算法的那个模板,因为那个模板只能查找是否有某个数,一旦 ...
- 直击--vue项目微信小程序页面跳转web-view不刷新-根源
背景 最近项目需要适配小程序,项目是使用了vue开发的网站,其中改造方式是,每个页面都使用小程序创建一个页面通过web-view来显示指定页面的. 在没有使用小程序时,路由跳转时,刷新页面等等,这个是 ...
- Codeforces 868E Policeman and a Tree
题意简述 给你一颗有n个点的树,每条边有边权,有一个警察一开始在点S,他的速度是1,即通过一条长度为x的边要花x单位时间. 有m个罪犯,一开始第i个在点x[i],他们的速度无限快. 如果罪犯和警察到达 ...
- wordpress搬家 更换域名
结论:wordpress网站文件夹是和域名相关联的 wordpress,备份了数据库 然后用另一个新域名新建站,直接从wordpress官网直接下载的网站压缩包,没有用之前的网站文件夹. 然后把原来的 ...
- 写论文的第四天 Spark安装 使用sparkshell
Spark分布式安装 Spark安装注意:需要和本机的hadoop版本对应 前往spark选择自己相对应的版本下载之后进行解压 命令:tar –zxf spark-2.4.0-bin-hadoop2. ...
- JMM内存模型详解(一)
本文开始死磕JMM(Java内存模型)由于知识点较多,分来写 该文为JMM第一篇 技术往往是枯燥的,本文文字较多 1. JMM是什么? 其实JMM很好理解,我简单的解释一下,在Java多线程中我们经常 ...
- 《Java 8 in Action》Chapter 3:Lambda表达式
1. Lambda简介 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. 匿名--我们说匿名,是因为 ...
- Oracle笔记_多表查询
1 执行sql文件 @文件地址名 --执行某个sql文件: 2 多表查询 想要的数据不在同一张表,就需要多个表进行联查. 多表查询也叫做表连接查询,其中的where条件就是连接条件. 可以使用join ...
- SQL优化没思路,智能优化工具来帮你
前言 作为DBA或系统管理员,我们有时会遇到一个慢SQL需要优化,但是通过分析执行计划又没有找到好的优化思路,或者优化之后效果不明显,没有达到自己理想的预期,此时的你是不是很焦虑?此时你一定想如果有一 ...