精华内容
下载资源
问答
  • 在项目开发时,我们可能会遇到这样的需求,只有一个域名,但是需要配置多个不同的模块项目进去,那么遇到这样的情况我们前端需要怎么配置呢?下面我给大家说下我的配置: 第一步 首先,找到vue项目中的config文件下...

    在项目开发时,我们可能会遇到这样的需求,只有一个域名,但是需要配置多个不同的模块项目进去,那么遇到这样的情况我们前端需要怎么配置呢?下面我给大家说下我的配置:

    第一步

    首先,找到vue项目中的config文件下的index.js,
    修改assetsPublicPath,具体配置如下:

    build: {
     	index: path.resolve(__dirname, '../dist/index.html'),
    	// Paths
        assetsRoot: path.resolve(__dirname, '../dist'),
        assetsSubDirectory: 'static',
        assetsPublicPath: '/test/'
    }
    

    第二步
    router文件下的index.js,配置如下:

    export default new Router({
        base: '/test',
        routes,
        mode: 'history'
    })
    

    第三步
    根文件index.html添加 <mate base="/test/" >

    以上配置结束打包就可以了!

    展开全文
  • 上线前准备这里我们同步演示怎么部署到win环境和linux(centos7)系统中,前端服务器采用nginx部署,并使用docker统一管理前后端服务器。所以我们会用到:nginxdocker compose希望你看视频前有点基础哈,当然了,这次...

    上线前准备

    这里我们同步演示怎么部署到win环境和linux(centos7)系统中,前端服务器采用nginx部署,并使用docker统一管理前后端服务器。

    所以我们会用到:

    • nginx

    • docker compose

    希望你看视频前有点基础哈,当然了,这次部署比较简单,不需要很精通,一般看了我的视频应该都能部署成功的哈。

    话不多说,直接上手!别忘了给我一个一键三联哈,顺便关注我B站,感谢!

    1、win环境

    win环境我就用本机来演示了,我们需要分别打包前后端,前后端打包都是一条命令即可,只不过我们打包之前注意需要配置好线上的环境参数等。

    1.1、前端

    先来配置一下后端的调用路径,因为现在部署在本地localhost,所以在axios.js中,我们配置好链接,因为等下后端部署也是本机,所以我这里直接这样配置就好了,如下:

    • src\axios.js

    axios.defaults.baseURL = "http://localhost:8081"

    上面配置的就是前端访问后端接口的服务。然后前端部署还需要考虑一个问题:打包之后项目资源引用路径,比如我们打包后链接是否需要带项目名称等。按照Vue官方的部署说明,我们添加一个vue.config.js文件,

    • vueblog-vue/vue.config.js

    module.exports = {
    publicPath: '/'
    }

    当然了,publicPath默认其实是空的,也就是publicPath: '',两个效果貌似其实是一样的,哈哈哈,我只是提醒一下有这回事而已,嘿嘿。设置完毕之后,我们执行打包命令:

    # 打包命令
    npm run build

    命令执行之后,我们在项目根目录下就可以找到一个dist的目录,这个就是打包之后的文件夹,里面有个index.html,但是我们点击直接打开是看不到任何内容的,这时候,我们需要部署到nginx中。 9873f7fcb48dc7d9a4ff4c1376e5b8df.png

    首先我们下载一个nginx,下载地址:http://nginx.org/en/download.html,这里我们下载nginx/Windows-1.18.0版本,下载之后解压zip。根据我们对nginx的熟悉,静态文件我们放在html文件夹下面,所以先把html文件夹中的index.html和50x.html删掉,然后把打包出来的dist文件夹内的所有文件都复制到nginx的html中,如图:

    2d126d94bdff85bad533d10ed294ff70.png

    双击nginx.exe启动nginx,然后浏览器输入http://localhost,出现了我们熟悉的界面,虽然没有博客数据,链接也自动跳转到了http://localhost/blogs,

    e61326ee7ae167d7672b5b374c349d1f.png

    我们点击任意一个链接或者按钮或者刷新界面,这时候出现了404:

    13d64a470bf2ab4c71cf1889c341c699.png

    刷新之后nginx就找不到路由了,这是为啥,得和你们科普一下,vue项目的入口是index.html文件,nginx路由的时候都必须要先经过这个文件,所以我们得给nginx定义一下规则,让它匹配不到资源路径的时候,先去读取index.html,然后再路由。所以我们配置一下nginx.conf文件。具体操作就是找到location /,添加上一行代码try_files $uri $uri/ /index.html last;如下:

    • nginx-1.18.0/conf/nginx.conf

    location / {
        root   html;
    try_files $uri $uri/ /index.html last;
        index  index.html index.htm;
    }

    这一行代码是什么意思呢?try_files的语法规则:格式1:try_files file ... uri,表示按指定的file顺序查找存在的文件,并使用第一个找到的文件进行请求处理,last表示匹配不到就内部直接匹配最后一个。

    重启nginx之后,链接再刷新都正常啦。但是没有数据,所以我们去部署一下后端。windows环境nginx的重启我一般都是打开任务管理器直接干掉nginx进程,然后再重新双击的~~

    dd3ce8f061e0941acbaa3a789d1299ab.png

    1.2、后端

    后端的打包就简单多了,应该大家都挺熟悉的,注意配置redis、mysql的链接密码啥的,然后执行命令,本机测试,redis和mysql我都已经提前安装好的了,sql文件也在vueblog-java的resources目录下。

    对了,pom.xml文件里面,spring-boot-maven-plugin之前注释掉了,现在一定要打开。不然执行jar会找不到主类。

    • pom.xml

    22e5d1d92d6f76c5abdc1c234fbe775e.png

    执行打包命令:

    # 跳过测试打包
    mvn clean package -Dmaven.test.skip=true

    得到target下的vueblog-0.0.1-SNAPSHOT.jar,然后再执行命令

    java -jar vueblog-0.0.1-SNAPSHOT.jar --spring.profiles.active=default

    后端上线之后,我们再访问下前端,发现已经可以正常浏览网页啦!spring.profiles.active表示指定环境配置文件。 712becddad03a09532972d570c223da8.png

    2、linux环境

    linux环境部署相对复杂一点,因为我们还要部署redis、mysql等。之前我发布过一个视频,是部署传统的博客项目eblog,采用的是docker容器,但是我们没有docker compose进行编排,这次我们使用docker compose来编排我们的服务,一起性搞定部署。

    二话不说,我们先来安装一下docker和docker compose,对于docker知识还不是特别懂的同学,建议自行去补习补习哈。

    2.1、安装docker

    #安装
    yum install docker
    #检验安装是否成功
    [root@localhost opt]# docker --version
    Docker version 1.13.1, build 7f2769b/1.13.1
    #启动
    systemctl start docker

    2.2、安装docker compose

    可以参考:https://docs.docker.com/compose/install/

    sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    #
    sudo chmod +x /usr/local/bin/docker-compose
    # 检查是否安装成功
    docker-compose --version

    2.3、编写Dockerfile文件

    因为我们的项目需要成为docker的镜像,所以我们必须先编写Dockerfile文件构建我们的项目镜像然后进行编排,Dockerfile文件可以帮我们进行构建。

    • Dockerfile

    FROM java:8
    EXPOSE 8080
    ADD vueblog-0.0.1-SNAPSHOT.jar app.jar
    RUN bash -c 'touch /app.jar'
    ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=pro"]

    上面几行命令其实很简单,首先依赖jdk8环境,对外暴露8080,然后就是复制vueblog-0.0.1-SNAPSHOT.jar到docker容器中并命名为app.jar,最后是执行命令java -jar /app.jar --spring.profiles.active=pro,使用的是我们另外编写的一个线上环境配置。

    • application-pro.yml

    # DataSource Config
    spring:
    datasource:
    driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: admin
    shiro-redis:
    enabled: true
    redis-manager:
    host: 127.0.0.1:6379

    等会儿我们需要修改application-pro.yml的redis和mysql链接等信息的一些配置,需要注意。

    2.4、编写docker-compose.yml文件

    我们需要用到的软件与服务分别有nginx、mysql、redis、还有我们的springboot项目,所以编写如下:

    • docker-compose.yml

    version: "3"
    services:
    nginx: # 服务名称,用户自定义
    image: nginx:latest # 镜像版本
    ports:
    - 80:80 # 暴露端口
    volumes: # 挂载
    - /root/nginx/html:/usr/share/nginx/html
    - /root/nginx/nginx.conf:/etc/nginx/nginx.conf
    privileged: true # 这个必须要,解决nginx的文件调用的权限问题
    mysql:
    image: mysql:5.7.27
    ports:
    - 3306:3306
    environment: # 指定用户root的密码
    - MYSQL_ROOT_PASSWORD=admin
    redis:
    image: redis:latest
    vueblog:
    image: vueblog:latest
    build: . # 表示以当前目录下的Dockerfile开始构建镜像
    ports:
    - 8081:8081
    depends_on: # 依赖与mysql、redis,其实可以不填,默认已经表示可以
    - mysql
    - redis

    上面的意思,我都用注释解释一遍了,希望可以讲清楚!需要注意的是,nginx中我们对nginx的放置静态资源的html文件夹和配置文件nginx.conf进行了一个挂载,所以我们打包后的文件放置到宿主机**/root/nginx/html**文件目录就行了哈

    2.5、修改application-pro.yml

    然后我们再回头看看application-pro.yml文件,mysql和redis的链接之前还是localhost,现在我们需要修改成容器之间的调用,如何知道mysql和redis的链接地址呢?docker compose就帮我们解决了这个问题,我们可以使用镜像容器的服务名称来表示链接。比如docker-compose.yml中mysql的服务名称就叫mysql、redis就叫redis。

    82f676e8c6ebd79416a9540d8d200030.png

    所以我们最终得到的配置文件如下:

    8a2ccba6cf2234b2d171cc49202f2bff.png

    2.6、准备好nginx的挂载目录和配置

    docker-compose.yml中已经提到,

    • 宿主机的挂载目录:/root/nginx/html

    • 挂载配置:/root/nginx/nginx.conf

    所以我们在root目录下新建nginx目录,并进入nginx目录下新建html目录和一个nginx.conf配置文件。

    9feda2f08f78f734b9068b29a7c6b157.png

    然后对nginx.conf进行编写,具体配置如下:

    • nginx.conf

    #user  root;
    worker_processes 1;
    events {
    worker_connections 1024;
    }
    http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;
    server {
    listen 80;
    server_name localhost;
    location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html last; # 别忘了这个哈
    index index.html index.htm;
    }
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root html;
    }
    }
    }

    2.7、上传前端

    前端打包之后先修改前端调用后端的接口,因为我是虚拟机,所以配置如下:

    • axios.js

    axios.defaults.baseURL = "http://192.168.0.117:8081"

    然后npm run build打包得到dist文件夹,并把dist压缩成dist.zip上传到linux之后解压到**/root/nginx/html**目录下。如下图: 825e5583fe1730dafd4db04bdab3c6fc.png

    2.8、部署后端

    一切准备就绪之后,我们就开始编排部署了哈。

    首先本地打包vueblog项目,vueblog-0.0.1-SNAPSHOT.jar,并上传到linux中,同时docker-compose.yml、Dockerfile也上传到同一目录下。如图所示:

    23c238c482edc6ddf571959a9affcc46.png

    然后我们执行一下编排命令:

    # 开始编排
    cd ~
    docker-compose up -d

    其中-d表示后台服务形式启动 56912f7a78d92d6de6e67d6a6a7eb53a.png

    然后我们稍等片刻,特别是开始Building vueblog的时候可能时间有点长,耐心等待即可!

    最后提示如下:

    fe548be0509652a29a6e1191d99a8f11.png

    说明我们已经成功编排啦。

    nginx是80端口,所以我们直接输入ip地址,如下可以看到一个界面然后有弹窗:

    ec2e57a375c55cfd5ca52b7f1257cea4.png

    这个简单,是因为我们的数据库还没创建哈。接下来我们去手动创建一下数据库并导入sql文件。

    • vueblog-java/src/main/resources/vueblog.sql

    ad09f2e244754ffb89bfd925a329407c.png

    然后再刷新一下浏览器链接,数据就出来啦,搞定,轻松

    展开全文
  • 怎么说呢,nodejs也是js,并且像阿里等大公司,别人虽然也用nginx之类,但是前端项目已经不是放在nginx上面跑了,所以,前端开发需要熟悉在nodejs环境下运行和开发。 项目已经上传 git地址:...

    项目背景

    这个是我个人博客下一步开发的流程,上一篇博客讲过,我会研究eggjs后端框架,给我的博客建立后端系统。

    这里有人会说前端不需要学后端。

    怎么说呢,nodejs也是js,并且像阿里等大公司,别人虽然也用nginx之类,但是前端项目已经不是放在nginx上面跑了,所以,前端开发需要熟悉在nodejs环境下运行和开发。

    项目已经上传

    git地址:https://github.com/ht-sauce/dream-backend

    调研

    仔细看了下eggjs官方的关于和vue项目的结合

    这里先奉上地址:https://www.yuque.com/easy-team/egg-vue/case

    egg官方分为三种渲染模式

    服务端渲染(ssr)

    说白了就是把vue当成视图模板去开发,和传统不同的在于是完全由前端控制,核心的路由是nodejs控制。这是和Nuxt.js通过vuerouter进行控制不一样。eggjs更多是变成了传统的mvc框架模式。前端变成了之前的view部分。

    前端渲染

    这个和nginx没啥太大差别,但是主要是eggjs官方自己将vue的开发环境和eggjs结合在一起了,不存在启动两个项目的情况 

    assets前端渲染

    这里我直接抄官方说法,说实在下面两点我没太理解。

    前端渲染模式 章节讲到了基于 Vue 的一体化的前端渲染模式,好处是不需要借助第三方模板引擎且无效关注静态资源注入问题,但有两个小的功能限制:

     

    • layout 模板数据绑定能力较弱

    • 资源注入不能自己定义,比如 async, crossorigin 等配置

    个人选择

    1、我个人而言项目之前是用的vuecli开发的,让我再把项目重构放在eggjs中,不乐意。

    2、如果从前端渲染角度去考虑,我觉得完全没必要把项目进行合并,也降低了项目复杂度,缺点就是项目变多。

    3、服务端渲染肯定是后期要做的。只不过先尝试。下次我会单独说一下服务端渲染。

    实战

    废话太多,最终我选择了前端渲染。选择和之前nginx类似的操作模式,不同在于我这次的vue项目是多页面项目。

    vue的多页面项目从实际角度来看,其实和以前的静态文件模式差不多,但是多页面的优点在于资源共享。

    1、打包

    注意一点,你到时候项目放在哪里,也就是你的资源文件访问是否存在前缀。

    我这里eggjs静态资源都在public下面所以,

    vue.config.js是这样的:

    //获取命令行参数
    const projectName = process.argv[3];
    console.log("当前打包项目名称:" + projectName);
    
    //页面配置参数,注意项目名称保持一致,页面结构保持一致
    const multiPageConfig = {
      //index是特殊的项目,不做多余操作,仅仅用于项目分发
      //举例,如判断电脑端和移动端,做首页项目入口
      index: {
        name: "海天酱油"
      },
      dht_blog: {
        name: "海天酱油博客"
      },
      dht_platform: {
        name: "海天酱油后台系统"
      }
    };
    //生成统一的页面配置结构
    const multiPage = function() {
      let page = {};
      for (let item in multiPageConfig) {
        page[item] = {
          entry: `src/${item}/main.js`,
          template: `src/${item}/index.html`,
          filename: `${item}.html`,
          title: multiPageConfig[item].name,
          favicon: `src/${item}/assets/${item}.ico`
        };
      }
      return page;
    };
    const page = multiPage();
    //vue下配置文件参数
    const vueConfig = {
    //注意,这里的public就是对于eggjs下的静态资源
      publicPath: process.env.NODE_ENV === "production" ? "/public" : "/", //部署应用包时的基本 URL
      outputDir: "dist", //打包目录
      pages: projectName ? page[projectName] : page
    };
    console.log(page);
    module.exports = vueConfig;
    

    打包之后直接放在eggjs下的app/public下

    然后关键的路由配置就像nginx一样

    这里注意,我开发的时候自己本身项目是区分了前缀的,但是eggjs的路由需要注意是在public下的,所以你需要增加public前缀,但是vue的router.js是不需要的

    vue:router.js

    eggjs:router.js

    注意访问下面两个路由的代码是没错的,但是在访问的时候

    应该分别是:/public/dht_blog/index

    /public/dht_platform/home

    原因在于我的项目在多页面的时候进行的首页重定向

    'use strict';
    
    /**
     * @param {Egg.Application} app - egg application
     */
    module.exports = app => {
      const { router, controller } = app;
      router.get('/', controller.home.index);
      router.get('/public/dht_blog/*', controller.home.dhtBlog);
      router.get('/public/dht_platform/*', controller.home.dhtPlatform);
    };
    

    接下来是eggjs的控制部分也就是mvc中的C(controller) ,直接读取静态文件发送给前端

    'use strict';
    const path = require('path');
    const fs = require('fs');
    const Controller = require('egg').Controller;
    
    class HomeController extends Controller {
      async index() {
        // 纯静态资源方式和eggjs结合,eggjs提供静态资源功能并且提供代理转发
        // 注意当多页面项目情况下需要配置多个前缀路由
        const { ctx } = this;
        ctx.response.type = 'html';
        ctx.body = fs.readFileSync(path.resolve(__dirname, '../public/index.html'));
      }
      async dhtBlog() {
        const { ctx } = this;
        ctx.response.type = 'html';
        ctx.body = fs.readFileSync(path.resolve(__dirname, '../public/dht_blog.html'));
      }
      async dhtPlatform() {
        const { ctx } = this;
        ctx.response.type = 'html';
        ctx.body = fs.readFileSync(path.resolve(__dirname, '../public/dht_platform.html'));
      }
    }
    
    module.exports = HomeController;
    

    接下来就是页面访问了

    展开全文
  • 项目地址 vue-cli3-project 欢迎 star 原文地址 https://www.ccode.live/lentoo/list/9?from=art1. 创建一个vue项目 相信大部分人都已经知道怎么创建项目的,可以跳过这一节,看下一节。1.1 安装@vue/cli# 全局安装 ...

    28815c4a635465b8252bf85fdb93a924.png
    项目地址 vue-cli3-project 欢迎 star
    原文地址 https://www.ccode.live/lentoo/list/9?from=art

    1. 创建一个vue项目

    相信大部分人都已经知道怎么创建项目的,可以跳过这一节,看下一节。

    1.1 安装@vue/cli

    # 全局安装 vue-cli脚手架
    npm install -g @vue/cli

    等待安装完成后开始下一步

    1.2 初始化项目

    vue create vue-cli3-project
    1. 选择一个预设

    e84b5a2c9a677c8d6d8fe0176b5c20a4.png

    可以选择默认预设,默认预设包含了babel,eslint

    我们选择更多功能 Manually select features

    回车后来到选择插件

    1. 插件选择

    这边选择了(Babel、Router、Vuex、Css预处理器、Linter / Formatter 格式检查、Unit测试框架)

    ea7db1c186fb94d6bed59c9cc8e6c92a.png
    1. 路由模式选择

    是否使用 history模式的路由 (Yes)

    c3ee70f6aa29d5c56c26397cabef7c3c.png
    1. 选择一个css预处理器 (Sass/SCSS)

    24fdb814046d8211ee2ff0f43adcac66.png
    1. 选择一个eslint配置

    这边选择 ESLint + Standard config,个人比较喜欢这个代码规范

    1cce99991df5bcaf0927d437f8fb4723.png
    1. 选择什么时候进行 eslint 校验

    选择(Lint on save)保存是检查

    如果你正在使用的vscode编辑器的话,可以配置eslint插件进行代码自动格式化

    70872131c2897f733a4174e70df4d0c8.png

    7. 选择测试框架 (Mocha + Chai)

    3010effcc10fa74c1a5cfa92168874f1.png

    8. 选择将这些配置文件写入到什么地方 (In dedicated config files)

    8e491f4ead54b26c843ae3bc0bc212e9.png
    1. 是否保存这份预设配置?(y)

    选是的话,下次创建一个vue项目,可以直接使用这个预设文件,而无需再进行配置。

    b69adbb67d9819bc87d9be2bb0c9cb86.png

    等待依赖完成

    b665812cd129ad29e13f15c2dfd8d5e8.png

    2. 全局组件自动注册

    components目录下创建一个global目录,里面放置一些需要全局注册的组件。

    index.js作用只要是引入main.vue,导出组件对象

    ea1d2a60cc4b1c1a7bc76db7a128a8d3.png

    components中创建一个index.js,用来扫描全局对象并自动注册。

    // components/index.js
    import Vue from 'vue'
    
    // 自动加载 global 目录下的 .js 结尾的文件
    const componentsContext = require.context('./global', true, /.js$/)
    
    componentsContext.keys().forEach(component => {
      const componentConfig = componentsContext(component)
      /**
      * 兼容 import export 和 require module.export 两种规范
      */
      const ctrl = componentConfig.default || componentConfig
      Vue.component(ctrl.name, ctrl)
    })
    

    最后在入口文件main.js中导入这个index.js中就可以了

    3.路由自动引入

    Vue项目中使用路由,相信想熟的人已经很熟悉怎么使用了,要新增一个页面的话,需要到路由配置中配置该页面的信息。

    如果页面越来越多的话,那么如何让我们的路由更简洁呢?

    3.1 拆分路由

    根据不同的业务模块进行拆分路由

    6eeeb9481d24c15490c8545795e6da17.png

    在每个子模块中导出一个路由配置数组

    ccb01f905b8b6b7938ed4b1e5dd728f3.png

    在根 index.js中导入所有子模块

    2792d3f6e07052db4feb7e6dc2d321c9.png

    3.2 自动扫描子模块路由并导入

    当我们的业务越来越庞大,每次新增业务模块的时候,我们都要在路由下面新增一个子路由模块,然后在index.js中导入。

    那么如何简化这种操作呢?

    通过上面的自动扫描全局组件注册,我们也可以实现自动扫描子模块路由并导入

    929010da4d6fadcf8b68dbd04b8929d9.png

    4. 通过node来生成组件

    作为前端开发者,放着 node这么好用的东西如果不能运用起来,岂不是很浪费?

    f89f6a229087c42719a99f6cb7cfa6be.png

    虽然我们通过上面已经实现了组件的自动注册,不过每次新建组件的时候,都要创建一个目录,然后新增一个.vue文件,然后写templatescriptstyle这些东西,然后新建一个index.js、导出vue组件、虽然有插件能实现自动补全,但还是很麻烦有木有。

    那么我们能不能通过node来帮助我们干这些事情呢?只要告诉node帮我生成的组件名称就行了。其它的事情让node来干

    905328218e30c5fee3a9808ab6155ae0.png

    4.1 通过node来生成组件

    • 安装一下chalk,这个插件能让我们的控制台输出语句有各种颜色区分
    npm install chalk --save-dev

    在根目录中创建一个 scripts 文件夹,

    新增一个generateComponent.js文件,放置生成组件的代码、

    新增一个template.js文件,放置组件模板的代码 * template.js

    // template.js
    module.exports = {
      vueTemplate: compoenntName => {
        return `<template>
      <div class="${compoenntName}">
        ${compoenntName}组件
      </div>
    </template>
    <script>
    export default {
      name: '${compoenntName}'
    }
    </script>
    <style lang="scss" scoped>
    .${compoenntName} {
    
    }
    </style>
    `
      },
      entryTemplate: `import Main from './main.vue'
    export default Main`
    }
    
    • generateComponent.js`
    // generateComponent.js`
    const chalk = require('chalk')
    const path = require('path')
    const fs = require('fs')
    const resolve = (...file) => path.resolve(__dirname, ...file)
    const log = message => console.log(chalk.green(`${message}`))
    const successLog = message => console.log(chalk.blue(`${message}`))
    const errorLog = error => console.log(chalk.red(`${error}`))
    const { vueTemplate, entryTemplate } = require('./template')
    
    const generateFile = (path, data) => {
      if (fs.existsSync(path)) {
        errorLog(`${path}文件已存在`)
        return
      }
      return new Promise((resolve, reject) => {
        fs.writeFile(path, data, 'utf8', err => {
          if (err) {
            errorLog(err.message)
            reject(err)
          } else {
            resolve(true)
          }
        })
      })
    }
    log('请输入要生成的组件名称、如需生成全局组件,请加 global/ 前缀')
    let componentName = ''
    process.stdin.on('data', async chunk => {
      const inputName = String(chunk).trim().toString()
      /**
       * 组件目录路径
       */
      const componentDirectory = resolve('../src/components', inputName)
    
      /**
       * vue组件路径
       */
      const componentVueName = resolve(componentDirectory, 'main.vue')
      /**
       * 入口文件路径
       */
      const entryComponentName = resolve(componentDirectory, 'index.js')
    
      const hasComponentDirectory = fs.existsSync(componentDirectory)
      if (hasComponentDirectory) {
        errorLog(`${inputName}组件目录已存在,请重新输入`)
        return
      } else {
        log(`正在生成 component 目录 ${componentDirectory}`)
        await dotExistDirectoryCreate(componentDirectory)
        // fs.mkdirSync(componentDirectory);
      }
      try {
        if (inputName.includes('/')) {
          const inputArr = inputName.split('/')
          componentName = inputArr[inputArr.length - 1]
        } else {
          componentName = inputName
        }
        log(`正在生成 vue 文件 ${componentVueName}`)
        await generateFile(componentVueName, vueTemplate(componentName))
        log(`正在生成 entry 文件 ${entryComponentName}`)
        await generateFile(entryComponentName, entryTemplate)
        successLog('生成成功')
      } catch (e) {
        errorLog(e.message)
      }
    
      process.stdin.emit('end')
    })
    process.stdin.on('end', () => {
      log('exit')
      process.exit()
    })
    function dotExistDirectoryCreate (directory) {
      return new Promise((resolve) => {
        mkdirs(directory, function () {
          resolve(true)
        })
      })
    }
    
    // 递归创建目录
    function mkdirs (directory, callback) {
      var exists = fs.existsSync(directory)
      if (exists) {
        callback()
      } else {
        mkdirs(path.dirname(directory), function () {
          fs.mkdirSync(directory)
          callback()
        })
      }
    }
    
    • 配置package.json
    "new:comp": "node ./scripts/generateComponent"
    • 执行

    如果使用 npm 的话 就是 npm run new:comp

    如果使用 yarn 的话 就是 yarn new:comp

    c210f0ad3680681f8e3f160a72dae7af.png

    4.2 通过node来生成页面组件

    通过上面的逻辑代码我们可以通过node来生成组件了,那么也可以举一反三来生成页面组件。只需稍微修改一下生成组件代码的逻辑。 在scripts目录下新建一个generateView.js文件

    // generateView.js
    const chalk = require('chalk')
    const path = require('path')
    const fs = require('fs')
    const resolve = (...file) => path.resolve(__dirname, ...file)
    const log = message => console.log(chalk.green(`${message}`))
    const successLog = message => console.log(chalk.blue(`${message}`))
    const errorLog = error => console.log(chalk.red(`${error}`))
    const { vueTemplate } = require('./template')
    
    const generateFile = (path, data) => {
      if (fs.existsSync(path)) {
        errorLog(`${path}文件已存在`)
        return
      }
      return new Promise((resolve, reject) => {
        fs.writeFile(path, data, 'utf8', err => {
          if (err) {
            errorLog(err.message)
            reject(err)
          } else {
            resolve(true)
          }
        })
      })
    }
    log('请输入要生成的页面组件名称、会生成在 views/目录下')
    let componentName = ''
    process.stdin.on('data', async chunk => {
      const inputName = String(chunk).trim().toString()
      /**
       * Vue页面组件路径
       */
      let componentVueName = resolve('../src/views', inputName)
      // 如果不是以 .vue 结尾的话,自动加上
      if (!componentVueName.endsWith('.vue')) {
        componentVueName += '.vue'
      }
      /**
       * vue组件目录路径
       */
      const componentDirectory = path.dirname(componentVueName)
    
      const hasComponentExists = fs.existsSync(componentVueName)
      if (hasComponentExists) {
        errorLog(`${inputName}页面组件已存在,请重新输入`)
        return
      } else {
        log(`正在生成 component 目录 ${componentDirectory}`)
        await dotExistDirectoryCreate(componentDirectory)
      }
      try {
        if (inputName.includes('/')) {
          const inputArr = inputName.split('/')
          componentName = inputArr[inputArr.length - 1]
        } else {
          componentName = inputName
        }
        log(`正在生成 vue 文件 ${componentVueName}`)
        await generateFile(componentVueName, vueTemplate(componentName))
        successLog('生成成功')
      } catch (e) {
        errorLog(e.message)
      }
    
      process.stdin.emit('end')
    })
    process.stdin.on('end', () => {
      log('exit')
      process.exit()
    })
    function dotExistDirectoryCreate (directory) {
      return new Promise((resolve) => {
        mkdirs(directory, function () {
          resolve(true)
        })
      })
    }
    
    // 递归创建目录
    function mkdirs (directory, callback) {
      var exists = fs.existsSync(directory)
      if (exists) {
        callback()
      } else {
        mkdirs(path.dirname(directory), function () {
          fs.mkdirSync(directory)
          callback()
        })
      }
    }
    
    • 配置package.json 新增一个scripts脚本
    "new:view": "node ./scripts/generateView"
    • 执行

    如果使用 npm 的话 就是 npm run new:view

    如果使用 yarn 的话 就是 yarn new:view

    0f6d13d5efa7de6e2272bc50dd804bf4.png

    5. axios封装

    • 安装 axios
    npm install axios --save
    // or
    yarn add axios

    5.1 配置不同的环境

    在根目录新建三个环境变量文件

    6b3ba972d0dfb65b568d2892ed0c4994.png

    分别输入不同的地址, 比如dev就写 dev的api地址、test就写test的api地址

    # // .env
    NODE_ENV = "development"
    BASE_URL = "https://easy-mock.com/mock/5c4c50b9888ef15de01bec2c/api"

    接着在根目录中新建一个 vue.config.js

    // vue.config.js
    module.exports = {
      chainWebpack: config => {
        // 这里是对环境的配置,不同环境对应不同的BASE_URL,以便axios的请求地址不同
        config.plugin('define').tap(args => {
          args[0]['process.env'].BASE_URL = JSON.stringify(process.env.BASE_URL)
          return args
        })
      }
    }

    然后在src目录下新建一个 api文件夹,创建一个 index.js用来配置 axios的配置信息

    // src/api/index.js
    import axios from 'axios'
    import router from '../router'
    import { Message } from 'element-ui'
    const service = axios.create({
      // 设置超时时间
      timeout: 60000,
      baseURL: process.env.BASE_URL
    })
    // post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置
    // 即设置post的请求头为application/x-www-form-urlencoded;charset=UTF-8
    service.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8''
    export default service
    

    5.2 请求响应封装

    import axios from 'axios'
    import router from '../router'
    import { Message } from 'element-ui'
    const service = axios.create({
      // 设置超时时间
      timeout: 60000,
      baseURL: process.env.BASE_URL
    })
    
    /**
     * 请求前拦截
     * 用于处理需要在请求前的操作
     */
    service.interceptors.request.use(config => {
      const token = localStorage.getItem('token')
      if (token) {
        config.headers['Authorization'] = token
      }
      return config
    }, (error) => {
      return Promise.reject(error)
    })
    /**
     * 请求响应拦截
     * 用于处理需要在请求返回后的操作
     */
    service.interceptors.response.use(response => {
      const responseCode = response.status
      // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
      // 否则的话抛出错误
      if (responseCode === 200) {
        return Promise.resolve(response)
      } else {
        return Promise.reject(response)
      }
    }, error => {
      // 服务器返回不是 2 开头的情况,会进入这个回调
      // 可以根据后端返回的状态码进行不同的操作
      const responseCode = error.response.status
      switch (responseCode) {
        // 401:未登录
        case 401:
          // 跳转登录页
          router.replace({
            path: '/login',
            query: {
              redirect: router.currentRoute.fullPath
            }
          })
          break
        // 403: token过期
        case 403:
          // 弹出错误信息
          Message({
            type: 'error',
            message: '登录信息过期,请重新登录'
          })
          // 清除token
          localStorage.removeItem('token')
          // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
          setTimeout(() => {
            router.replace({
              path: '/login',
              query: {
                redirect: router.currentRoute.fullPath
              }
            })
          }, 1000)
          break
        // 404请求不存在
        case 404:
          Message({
            message: '网络请求不存在',
            type: 'error'
          })
          break
        // 其他错误,直接抛出错误提示
        default:
          Message({
            message: error.response.data.message,
            type: 'error'
          })
      }
      return Promise.reject(error)
    })
    
    export default service

    Message 方法是 element-ui 提供的一个消息提示组件、大家可以根据自己的消息提示组件进行替换

    5.3 断网处理

    在响应拦截中添加处理逻辑

    service.interceptors.response.use(response => {
      const responseCode = response.status
      // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
      // 否则的话抛出错误
      if (responseCode === 200) {
        return Promise.resolve(response.data)
      } else {
        return Promise.reject(response)
      }
    }, error => {
      // 断网 或者 请求超时 状态
      if (!error.response) {
        // 请求超时状态
        if (error.message.includes('timeout')) {
          console.log('超时了')
          Message.error('请求超时,请检查网络是否连接正常')
        } else {
          // 可以展示断网组件
          console.log('断网了')
          Message.error('请求失败,请检查网络是否已连接')
        }
        return
      }
      // 省略其它代码 ······
      return Promise.reject(error)
    })
    

    5.4 封装图片上传

    // src/api/index.js
    export const uploadFile = formData => {
      const res = service.request({
        method: 'post',
        url: '/upload',
        data: formData,
        headers: { 'Content-Type': 'multipart/form-data' }
      })
      return res
    }
    

    调用

    async uploadFile (e) {
      const file = document.getElementById('file').files[0]
      const formdata = new FormData()
      formdata.append('file', file)
      await uploadFile(formdata)
    }
    

    5.5 请求 显示 Loading 效果

    let loading = null
    service.interceptors.request.use(config => {
      // 在请求先展示加载框
      loading = Loading.service({
        text: '正在加载中......'
      })
      // 省略其它代码 ······
      return config
    }, (error) => {
      return Promise.reject(error)
    })
    service.interceptors.response.use(response => {
      // 请求响应后关闭加载框
      if (loading) {
        loading.close()
      }
     // 省略其它代码 ······
    }, error => {
      // 请求响应后关闭加载框
      if (loading) {
        loading.close()
      }
      // 省略其它代码 ······
      return Promise.reject(error)
    })
    

    6. 巧用 Mixins

    e54c8d253684b09e248271311b735d6a.png

    6.1 封装 store 公用方法

    假设有这样一个场景,我们通过 vuex 封装了获取新闻列表的 function

    import Vue from 'vue'
    import Vuex from 'vuex'
    import { getNewsList } from '../api/news'
    Vue.use(Vuex)
    const types = {
      NEWS_LIST: 'NEWS_LIST'
    }
    export default new Vuex.Store({
      state: {
        [types.NEWS_LIST]: []
      },
      mutations: {
        [types.NEWS_LIST]: (state, res) => {
          state[types.NEWS_LIST] = res
        }
      },
      actions: {
        [types.NEWS_LIST]: async ({ commit }, params) => {
          const res = await getNewsList(params)
          return commit(types.NEWS_LIST, res)
        }
      },
      getters: {
        getNewsResponse (state) {
          return state[types.NEWS_LIST]
        }
      }
    })
    

    然后在新闻列表页,我们通过 mapActionmapGetters来调用Actiongetters 我们需要写上这些代码

    import { mapActions, mapGetters } from 'vuex'
    
    computed: {
        ...mapGetters(['getNewsResponse'])
    },
    methods: {
        ...mapActions(['NEWS_LIST'])
    }

    在假设,在另一个页面又需要重新调用获取新闻列表的接口,我们又要在写一遍上面的代码对吧?

    复制粘贴就是干有木有?

    如果接口突然加了一个参数,那岂不是每个要用到这个接口的代码都得加这个参数。

    复制粘贴一时爽,需求一改你就爽

    de1669951ea0558f64c6712a83b54ee8.png

    既然是重复的代码,我们肯定要复用,这时候Vue提供的Mixin就起了大作用了 * 封装 news-mixin.js 在 src下创建一个mixins目录,用来管理所有的mixins 新建一个news-mixin.js

    import { mapActions, mapGetters } from 'vuex'
    export default {
      computed: {
        ...mapGetters(['getNewsResponse'])
      },
      methods: {
        ...mapActions(['NEWS_LIST'])
      }
    }
    

    然后在需要用到的组件中引入这个mixin,就能直接调用这个方法了。不管多少个页面,只要引入这个mixin,直接就能使用。

    需求一改的话,也只需要修改这个mixin文件

    // news/index.vue
    import Vue from 'vue'
    import newsMixin from '@/mixins/news-mixin'
    export default {
      name: 'news',
      mixins: [newsMixin],
      data () {
        return {}
      },
      async created () {
        await this.NEWS_LIST()
        console.log(this.getNewsResponse)
      }
    }
    

    6.2 扩展

    除了封装 vuex 的公用方法,其实还有很多的东西也能做封装。例如:分页对象,表格数据,公用方法、等等就不一一举例了。可以看github

    在多个地方经常使用,就可以考虑封装成mixin,不过请写好注释哦。不然就会有人在背后骂你了!!你懂的~~

    7. 优化

    7.1 gzip压缩

    • 安装compression-webpack-plugin插件
    npm install compression-webpack-plugin --save-dev
    // or
    yarn add compression-webpack-plugin --dev
    • 在 vue.config.js 中添加配置
    // vue.config.js
    const CompressionPlugin = require('compression-webpack-plugin')
    module.exports = {
      chainWebpack: config => {
        // 这里是对环境的配置,不同环境对应不同的BASE_URL,以便axios的请求地址不同
        config.plugin('define').tap(args => {
          args[0]['process.env'].BASE_URL = JSON.stringify(process.env.BASE_URL)
          return args
        })
        if (process.env.NODE_ENV === 'production') {
          // #region 启用GZip压缩
          config
            .plugin('compression')
            .use(CompressionPlugin, {
              asset: '[path].gz[query]',
              algorithm: 'gzip',
              test: new RegExp('.(' + ['js', 'css'].join('|') + ')$'),
              threshold: 10240,
              minRatio: 0.8,
              cache: true
            })
            .tap(args => { })
    
          // #endregion
        }
      }
    }

    npm run build后能看到生成 .gz 文件就OK了。如果你的服务器使用nginx的话,nginx也需要配置开启GZIP、下面会讲到如何在nginx中开启GZIP

    f0ffda4b9697e144f9f9ece8793a3780.png

    7.2 第三方库引用cdn

    对于 vuevue-routervuexaxioselement-ui等等这些不经常改动的库、我们让webpack不对他们进行打包,通过cdn引入,可以减少代码的大小、也可以减少服务器的带宽,更能把这些文件缓存到客户端,客户端加载的会更快。 * 配置vue.config.js

    const CompressionPlugin = require('compression-webpack-plugin')
    module.exports = {
      chainWebpack: config => {
          // 省略其它代码 ······
          // #region 忽略生成环境打包的文件
    
          var externals = {
            vue: 'Vue',
            axios: 'axios',
            'element-ui': 'ELEMENT',
            'vue-router': 'VueRouter',
            vuex: 'Vuex'
          }
          config.externals(externals)
        const cdn = {
            css: [
              // element-ui css
              '//unpkg.com/element-ui/lib/theme-chalk/index.css'
            ],
            js: [
              // vue
              '//cdn.staticfile.org/vue/2.5.22/vue.min.js',
              // vue-router
              '//cdn.staticfile.org/vue-router/3.0.2/vue-router.min.js',
              // vuex
              '//cdn.staticfile.org/vuex/3.1.0/vuex.min.js',
              // axios
              '//cdn.staticfile.org/axios/0.19.0-beta.1/axios.min.js',
              // element-ui js
              '//unpkg.com/element-ui/lib/index.js'
            ]
          }
          config.plugin('html')
            .tap(args => {
              args[0].cdn = cdn
              return args
            })
          // #endregion
        }
      }
    }
    • 修改index.html
    <!--public/index.html-->
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <% if (process.env.NODE_ENV === 'production') { %>
    
          <% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
            <link href="<%=css%>" rel="preload" as="style">
            <link rel="stylesheet" href="<%=css%>" as="style">
          <% } %>
          <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
            <link href="<%=js%>" rel="preload" as="script">
            <script src="<%=js%>"></script>
          <% } %>
    
        <% } %>
        <title>vue-cli3-project</title>
      </head>
      <body>
        <noscript>
          <strong>We're sorry but vue-cli3-project doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
        </noscript>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>

    7.3 全站cdn

    我们已经把第三方库使用cdn替代了,那么我们build后生成的 js,css之类的文件能否也用cdn呢?

    ab745b915e7839561c2ebc27bf842f95.png

    申请自己的cdn域名

    要想把自己的资源上传到cdn上,前提是得有自己的cdn域名,如果没有的话,可以到七牛云官网上注册申请一个 1. 注册七牛云账号 2. 到七牛云对象存储模块中新建存储空间 3. 输入存储空间信息

    a460b15b5ad2fd5bcea40d41c2fe0f8a.png

    4. 确定创建 5. 创建成功后会跳转到这个存储空间的控制台页面

    0598e89de5e456b9ee801991e551df75.png

    6. 其中有个域名就是你的测试域名 7. 我们可以在内容管理那上传我们的jscss之类的文件、不过我们的文件那么多,一个一个上传明显不合理。要你你也不干。

    这时候,这些批量又重复的操作应该由我们的node出马,让我们来通过 node来批量上传我们的资源文件

    将生成的js、css资源上传到七牛cdn

    在七牛云官网的文档中心有介绍如何通过node上传文件、感兴趣的人可以自己去研究一下。

    1. 查看AccessKeySecretKey,在你的个人面板 -> 秘钥管理 ,这两个秘钥待会会用到

    6d9652cb719e2e55176f0924b85d7b3b.png
    1. 安装需要的插件
    npm install qiniu glob mime --save-dev
    1. scripts目录下创建一个 upcdn.js 文件
    // /scripts/upcdn.js
    const qiniu = require('qiniu')
    const glob = require('glob')
    const mime = require('mime')
    const path = require('path')
    
    const isWindow = /^win/.test(process.platform)
    
    let pre = path.resolve(__dirname, '../dist/') + (isWindow ? '' : '')
    
    const files = glob.sync(
      `${path.join(
        __dirname,
        '../dist/**/*.?(js|css|map|png|jpg|svg|woff|woff2|ttf|eot)'
      )}`
    )
    pre = pre.replace(//g, '/')
    
    const options = {
      scope: 'source' // 空间对象名称
    }
    var config = {
      qiniu: {
        accessKey: '',  // 个人中心 秘钥管理里的 AccessKey
        secretKey: '',  // 个人中心 秘钥管理里的 SecretKey
        bucket: options.scope,
        domain: 'http://ply4cszel.bkt.clouddn.com'
      }
    }
    var accessKey = config.qiniu.accessKey
    var secretKey = config.qiniu.secretKey
    
    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
    var putPolicy = new qiniu.rs.PutPolicy(options)
    var uploadToken = putPolicy.uploadToken(mac)
    var cf = new qiniu.conf.Config({
      zone: qiniu.zone.Zone_z2
    })
    var formUploader = new qiniu.form_up.FormUploader(cf)
    async function uploadFileCDN (files) {
      files.map(async file => {
        const key = getFileKey(pre, file)
        try {
          await uploadFIle(key, file)
          console.log(`上传成功 key: ${key}`)
        } catch (err) {
          console.log('error', err)
        }
      })
    }
    async function uploadFIle (key, localFile) {
      const extname = path.extname(localFile)
      const mimeName = mime.getType(extname)
      const putExtra = new qiniu.form_up.PutExtra({ mimeType: mimeName })
      return new Promise((resolve, reject) => {
        formUploader.putFile(uploadToken, key, localFile, putExtra, function (
          respErr,
          respBody,
          respInfo
        ) {
          if (respErr) {
            reject(respErr)
          }
          resolve({ respBody, respInfo })
        })
      })
    }
    function getFileKey (pre, file) {
      if (file.indexOf(pre) > -1) {
        const key = file.split(pre)[1]
        return key.startsWith('/') ? key.substring(1) : key
      }
      return file
    }
    
    (async () => {
      console.time('上传文件到cdn')
      await uploadFileCDN(files)
      console.timeEnd('上传文件到cdn')
    })()
    

    修改 publicPath

    修改vue.config.js的配置信息,让其publicPath指向我们cdn的域名

    const IS_PROD = process.env.NODE_ENV === 'production'
    const cdnDomian = 'http://ply4cszel.bkt.clouddn.com'
    module.exports = {
      publicPath: IS_PROD ? cdnDomian : '/',
      // 省略其它代码 ·······
    }

    修改package.json配置

    修改package.json配置,使我们build完成后自动上传资源文件到cdn服务器

    "build": "vue-cli-service build --mode prod && node ./scripts/upcdn.js",

    运行查看效果

    npm run build

    4235fd6805b14060781ec0b64ce77a8e.png

    然后到你的cdn控制台的内容管理看看文件是否已经上传成功

    07ae4a63712c9f8ff01a9f19a7c43ef5.png

    67187c78c31f310b5fef766b96526670.png

    8. docker部署

    这边使用的是 centOS7 环境,不过使用的是不同的系统,可以参考一下其它系统的安装方法

    8.1 安装docker

    • 更新软件库
    yum update -y
    • 安装docker
    yum install docker
    • 启动docker服务
    service docker start
    • 安装docker-compose
    // 安装epel源
    yum install -y epel-release
    // 安装docker-compose
    yum install docker-compose

    8.2 编写docker-compose.yaml

    version: '2.1'
    services:
      nginx:
        restart: always
        image: nginx
        volumes:
          #~ /var/local/nginx/nginx.conf为本机目录, /etc/nginx为容器目录
          - /var/local/nginx/nginx.conf:/etc/nginx/nginx.conf
          #~ /var/local/app/dist 为本机 build 后的dist目录, /usr/src/app为容器目录,
          - /var/local/app/dist:/usr/src/app
        ports:
          - 80:80
        privileged: true

    8.3 编写 nginx.conf 配置

    #user  nobody;
    
    worker_processes  2;
    
    #工作模式及连接数上线
    events {
        worker_connections  1024;   #单个工作进程 处理进程的最大并发数
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用,
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        # 开启GZIP
        gzip  on;
    
        # # 监听 80 端口,转发请求到 3000 端口
        server {
            #监听端口
            listen      80;
            #编码格式
            charset utf-8;
    
            # 前端静态文件资源
            location / {
            root  /usr/src/app;
                index index.html index.htm;
                try_files $uri $uri/ @rewrites;
            }
            # 配置如果匹配不到资源,将url指向 index.html, 在 vue-router 的 history 模式下使用,就不会显示404
            location @rewrites {
                rewrite ^(.*)$ /index.html last;
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }

    8.4 执行 docker-compose

    docker-compose -d up

    1cc308fbd6d59922feae404d00661386.png

    8.5 docker + jenkins 自动化部署

    使用docker + jenkins 能实现代码提交到github后自动部署环境、这个要讲起来内容太多,有兴趣的可以看我这一篇文章

    从零搭建docker+jenkins+node.js自动化部署环境

    6. 扩展

    • 使用pm2自动化部署node项目
    • 通过vue-cli3构建一个SSR应用程序
    如果大家还有什么更好的实践方式,欢迎评论区指教!!

    e0ab3b0f23b6add5dd2255a381a03079.png
    项目地址 vue-cli3-project 欢迎 star
    原文地址 https://www.ccode.live/lentoo/list/9?from=art

    欢迎关注

    欢迎关注公众号“码上开发”,每天分享最新技术资讯

    8f8140658660e85193f845568ace3d5d.png
    展开全文
  • 这两天想试一下怎么部署vue项目,可怜我这小白一点经验都没,踩了很坑,以此记录一下我配置的过程,希望能帮到其他人。对很原理不了解,希望大佬们能够指出。 配置情况说明: 软件 说明 服务器:...
  • 最近开发项目一直遇到一问题就是vue2的项目打包之后的静态资源怎么部署在tomcat 经过一段时间的研究后,最后解决了这问题,现在分享给打下 第一步 就是执行 npm run dev 第二步 执行 npm run build 这...
  • 怎么在一台主机上面部署多个网站 使用Nginx的虚拟化配置 环境 使用一键安装lnmp环境请参考 https://segmentfault.com/a/11... 第一步:执行 lnmp vhost add lnmp vhost add 输入自己要绑定的域名,比如我现在需要...
  • 进入该vue项目目录,打开git bash,执行:npm run build(在package.json的scripts配置)执行成功如下图所示:然后此时你会发现项目下了一 dist 文件夹,dist下文件便是项目打包之后生成的文件。此时我们直接在...
  • 亲测,webpack打包vue项目之后生成的dist文件可以部署到 express 服务器上运行。 我的vue项目结构如下: 1. 进入该vue项目目录,打开git bash,执行:npm run build(在package.json的scripts配置) 执行成功如下...
  • 1. 创建一个vue项目 相信大部分人都已经知道怎么创建项目的,可以跳过这一节,看下一节。 1.1 安装@vue/cli # 全局安装 vue-cli脚手架 npm install -g @vue/cli 等待安装完成后开始下一步 1.2 初始化项目 vue ...
  • 进入该vue项目目录,打开git bash,执行:npm run build(在package.json的scripts配置)执行成功如下图所示:然后此时你会发现项目下了一 dist 文件夹,dist下文件便是项目打包之后生成的文件。此时我们直接在...
  • 推荐在这里阅读效果更佳 背景 网上搜了很教程,包括官网的教程,但是...你的 vuepress 项目是否已经在github上的仓库下 如果是,请跳到第二步 如果不是,想在GitHub新建一仓库,如图所示: ## 小坑 仓库名字要和...
  • 时候,一个vue项目打包下来,app.js 主文件都会有几兆,大点的项目都会有十几二十兆,还有app.css的打包文件可能也会很大。 对于一般的服务器来说,首次去打开部署好的平台页面,加载起来会很慢。 这样的速度...
  • 手上有个项目vue+django的模式,需求是vue部署到一台服务器上,django部署到另外一台服务器上 第一步:安装nginx.......怎么安装请谷歌,在此不做赘述(有很类似的教程) 第二步:找到nginx的配置文件:/etc/...
  • vue-cli怎么和express搭建项目

    千次阅读 2018-10-26 16:03:57
    1.其实自动解除vue一来,就一直在想这问题,问题是:express后台有一服务是3000端口,然后vue-cli又开启了一8080端口, 那么怎么把他们两结合在一起呢? 要跨域吗? 部署到服务器的时候应该运行这npm run dev的...
  • 使用 Nginx 部署前后端分离项目,解决跨域问题

    千次阅读 多人点赞 2019-09-24 09:50:28
    前后端分离这问题其实松哥和大家聊过很了,上周松哥把自己的两开源项目部署在服务器上以帮助大家可以快速在线预览(喜大普奔,两开源的 Spring Boot + Vue 前后端分离项目可以在线体验了),然后群里就有小...
  • Gin-vue-admin是一基于vue和gin开发的全栈前后端分离的后台管理系统,集成jwt鉴权,动态路由,动态菜单,casbin鉴权,表单生成器,代码生成器等功能,提供多种示例文件,让您把更时间专注在业务开发上。...
  • 如果让一专业的前端工程师来写前端页面,其实也不难,Vue 算是三大前端框架中最容易上手的了。那怎么样就有难度了呢?让同一个人既写前端又写后端!我知道很小伙伴在这里总是想不通,前后端是怎么通信的?跨域是...
  • 自己在写公司项目时封装了这组件,它省去了初始化 UEditor、手动调用 getContent,setContent 等繁琐的操作,而是直接采用 v-model 来绑定数据,使得在 Vue 项目中的使用UEditor 可以像 Input 框一样简单。...
  • 如果让一专业的前端工程师来写前端页面,其实也不难,Vue 算是三大前端框架中最容易上手的了。那怎么样就有难度了呢?让同一个人既写前端又写后端!我知道很小伙伴在这里总是想不通,前后端是怎么通信的?跨域是...
  • DncZeus前后端分离项目打包/发布/部署及注意事项前言DncZeus这基于ASP.NET Core + Vue.js前后端分离的通用后台管理框架从发布到现在已有大半年时间了,期间很使用者问到DncZeus项目怎么打包,怎么发布,怎么部署...
  • 我们之前的文章,用springboot+vue+redis简单写了一前后端分离的后台管理系统。 那么问题来了。如果要部署到阿里云服务器怎么弄呢。如果是传统的部署方式,肯定是要在服务器按照顺序安装数据库mysql,tocat,nginx...
  • 想要部署mocker平台,就在朋友的推荐下选择了 api-mocker 这现成的项目项目分为服务端node、客户端vue、以及数据库mongoDB 在尝试直接部署的时候发现需要装一大堆的环境,node、mongo、nginx啊,特别的麻烦...
  • 最终会将整个前端项目拆解成一个主项目多个项目,其中两者作用如下: <ul><li> 主项目:用于管理子项目的路由切换、注册子项目的路由和全局 Store 层、提供全局库和方法 </li><li> 子项目&#...
  • 我自己的前端学的不怎么样,可以说是半吊子,只能粗略的用一下,文章主要是针对那些对后端比较了解,但是前端了解的比较少,只需要会用就行的人,帮你们少走点弯路,都是自己踩了很坑走过来的。 前面几段是根据...
  • nginx + ubuntu 部署踩坑

    2020-07-07 12:05:03
    最近在Ubuntu上部署springboot + vue.js 的项目的时候遇到了两坑。 1 配置nginx配置文件的时候,默认的配置文件会引入sites-enabled 目录下的配置文件,这文件里面指定的主页是 /usr/shar/nginx/...

空空如也

空空如也

1 2 3
收藏数 49
精华内容 19
关键字:

怎么部署多个vue项目

vue 订阅