p3-m3

  • 本阶段围绕当下国内最主流的前端核心框架 Vue.js 展开,深入框架内部,通过解读源码或者手写实现的方式,剖析 Vue.js 框架的内部实现原理,让你做到知其所以然。同时我们还会介绍 Vue.js 的进阶用法、周边生态以及性能优化,让你轻松应对更加复杂的项目业务需求。

模块三 Vuex 数据流管理及Vue.js 服务端渲染(SSR)

  • 本模块会介绍关于复杂项目中的状态管理方案 Vuex,以及自己来手写一个自己的 Vuex。Vue.js 中服务端渲染的使用尽在这里,本模块会先告诉你为什么要使用 SSR,紧接着带你使用 SSR 框架 Nuxt.js 快速开发一个服务端渲染的项目。
任务一:Vuex 状态管理
  1. 课程目标
  • Vue 组件间通信方式回顾
  • Vuex 核心概念和基本使用回顾
  • 购物车案例
  • 模拟实现 Vuex
  1. 组件内的状态管理流程
  • state 驱动应用的数据源
  • view 以声明方式将state映射到视图
  • actions 相应在view 上的用户输入导致的状态改变
  1. 组件间通信方式回顾-父组件给子组件传值
  • 父组件给子组件穿值
    • 子组件中通过props接收数据
    • 父组件中给子组件通过相应属性传值
  1. 组件间通信方式回顾-子组件给父组件传值

  2. 组件间通信方式回顾-不相关组件传值

// eventbus.js
import Vue from 'vue'
export default new Vue()

import bus from './eventbus.js'
// bus.$emit() bus.$on()
  1. 组件间通信方式回顾-通过 ref 获取子组件
  • 其它常见方式
    • $root
    • $parent
    • $children
    • $refs
      • 在普通标签上使用ref,获取的是DOM
      • 在组件标签上使用ref,获取到的是组件实例
  1. 简易的状态管理方案
  • 问题
    • 多个视图依赖同一状态
    • 来自不同视图的行为需要变更同一状态
  1. Vuex 概念回顾
  • 什么是Vuex
    • Vuex 是专门为Vue.js设计的状态管理库
    • Vuex 采用集中式的方式存储需要共享的状态
    • Vuex 的作用是进行状态管理,解决复杂组件通信,数据共享
    • Vuex 集成到了 devtools 中,提供了time-travel时光旅行历史回滚功能
  • 什么情况下使用Vuex
    • 非必要的情况不要使用Vuex
    • 大型的单页应用程序
      • 多个视图依赖同一状态
      • 来自不同视图的行为需要变更同一状态
  1. Vuex 的核心概念

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TDQu1gVm-1627096149838)(./img/1626770990920.jpg)]

  • Store 每一个应用仅有一个Store,Store是一个容器,包含应用中的大部分状态,不能直接改变Store中的状态
  • State 就是单一状态树,所有的状态都保存在State,会让程序难以维护,可以通过模块解决该问题,这里的状态是响应式的
  • Getter 就像Vue中的计算属性,方便从一个属性派生出其它值,它内部可以对计算结果进行缓存,只有当依赖的状态发生改变才重新计算
  • Mutation 状态的变化必须提交Mutation完成
  • Action 和Mutation类似,不同的是,Action可以进行异步操作,内部改变状态的时候都需要提交 Mutation
  • Module 由于使用单一状态树,应该的状态会集中在一个对象上,当应用复杂时,Store对象就可以变得非常臃肿,为了解决这个问题,Vuex 允许我们将 Store 分为模块,每个模块拥有直接的State,Mutation,Action,Getter
  1. Vuex 的核心概念
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
    state: {},
    mutations: {},
    actions: {},
    modules: {}
})

import store from './store'
new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')
  1. State

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQy1pKew-1627096149839)(./img/1626772526939.jpg)]

  1. Getter
<!-- {{ $store.getters.reverseMsg }} -->
{{reverseMsg}}
export default new Vuex.Store({
    state: {
        msg: 'abc'
    },
    mutations: {},
    getters: {
      reverseMsg(state) {
          return state.msg.split('').reverse().join('')
      }
    },
    actions: {},
    modules: {}
})

import { mapGetters } from 'vuex'
export default {
    computed: {
        ...mapGetters(['reverseMsg'])
    }
}
  1. Mutation
<!-- <button @click="$store.commit('increate', 2)"></button> -->
<!-- <button @click="increate(2)"></button> -->
 <!-- <button @click="$store.dispatch('increateAsync', 5)"></button> -->
  <button @click="increateAsync(6)">Action</button>
export default new Vuex.Store({
    state: {
        count: 0,
        msg: 'abc'
    },
    mutations: {
        increate(state, payload) {
            state.count += payload
        }
    },
    getters: {
      reverseMsg(state) {
          return state.msg.split('').reverse().join('')
      }
    },
    actions: {
        increateAsync(context, payload) {
            setTimeout(() => {
                context.commit('increate', payload)
            }, 2000)
        }
    },
    modules: {}
})

import { mapMutations } from 'vuex'
export default {
    methods: {
        ...mapMutations(['increate']),
        ...mapActions(['increateAsync'])
    }
}
  1. Action

  2. Module

```html
<!-- {{ $store.state.cart.cart }} -->
{{ cart }}
```
```js
// cart.js
const state = {}
const getters = {}

export default {
    namespaced: true, // 开启命名空间
    state: {
        cart: 2
    },
    getters
}

// index.js
import cart from './cart.js'

export default new Vuex.Store({
  strict: true, // 开启严格模式
  state: {
  },
  mutations: {
  },
  getters: {
  },
  actions: {
  },
  modules: {
    cart
  }
})

import { mapState } from 'vuex'
export default {
    computed: {
        ...mapState('cart', ['cart'])
    }
}
```
  1. 严格模式
  • 如果修改state没有通过mutations,那么devtools就不能跟踪到,这是一种约定
  • 严格模式,直接修改state状态,会抛出错误,但数据会被修改。
  • 不要在生成环境开启严格模式,严格模式会深度检查状态树,来检查不合规的状态改变,会影响性能
  • strict: process.env.NODE_ENV !== ‘production’ 只在生产环境不开启严格模式。开发环境开启
  1. 购物车案例-演示

  2. 购物车案例 - 模板

  • 模版地址
    • https://github.com/goddlts/vuex-cart-demo-template.git
  1. 购物车案例 - 商品列表

  2. 购物车案例 - 添加购物车

  3. 购物车案例 - 我的购物车 - 列表

  4. 购物车案例 - 我的购物车 - 统计

  5. 购物车案例 - 我的购物车 - 删除

  6. 购物车案例 - 购物车组件 - 购物车列表

  7. 购物车案例 - 购物车组件 - 全选

  8. 购物车案例 - 购物车组件 - 数字文本框

  9. 购物车案例 - 购物车组件 - 统计

  10. 购物车案例-本地存储

  • Vuex 插件介绍
    • Vuex 的插件就是一个函数
    • 这个函数接收一个store的参数
    const myPlugin = store => {
        // 当 stroe 初始化后调用
        store.subscribe((mutation, state) => {
            //每次 mutation之后调用
            // mutatuin 的格式为 { type, payload }
        })
    }
    
    export default new Vuex.Store({
      state: {
          msg: 'abc'
      },
      mutations: {},
      getters: {
        reverseMsg(state) {
            return state.msg.split('').reverse().join('')
        }
      },
      actions: {},
      modules: {},
      plugins: [myPlugin]
    })
    
  1. 模拟 Vuex - 基本结构

  2. 模拟 Vuex - install

  3. 模拟 Vuex - Store 类

let _Vue = null
class Store {
    constructor(options) {
        const {
            state = {},
            getters = {},
            mutations = {},
            actions = {}
        } = options
        this.state = _Vue.observable(state)
        this.getters = Object.create(null)
        Object.keys(getters).forEach(key => {
            Object.defineProperty(this, key, {
                get() => getters[key](state)
            })
        })
        this._nutations = mutations
        this._actions = actions
    }
    commit(type, payload) {
        this._nutations[type](this.state, payload)
    }
    dispatch(type, payload) {
        this._actions[type](this, payload)
    }
}
function install (Vue) {
    _Vue = Vue
    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                _Vue.prototype.$store = this.$options.store
            }
        }
    })
}
export default {
    Store,
    install
}
任务二:服务端渲染基础
  1. 概述
  • SPA 单页面应用
    • 优点 用户体验好 开发效率高 渲染性能好 可维护性好
    • 缺点 首屏渲染时间长 不利于SEO
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AkeNHqgJ-1627096149840)(./img/1626854774759.jpg)]
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSC1lmGn-1627096149842)(./img/1626854851027.jpg)]
  • 同构应用
    • 通过服务端渲染首屏直出,解决SPA应用首屏渲染慢以及不利于SEO问题
    • 通过客户端渲染接管页面内容交互得到更好的用户体验
    • 这种方式通常称之为现代化的服务端渲染,也叫同构渲染
    • 这种方式构建的应用称之为服务端渲染应用或者是同构应用
  1. 什么是渲染
  • 渲染:把 数据 + 模版 拼接到一起
  1. 传统的服务端渲染
  • nodemon 文件名 对比node,修改js文件后自动重启
  • 网页越来越复杂的情况下,存在很多不足
    • 前后端代码完全耦合在一起,不利于开发和维护
    • 前端没有足够发挥空间
    • 服务端压力大
    • 用户体验一般
      • 只有经过刷新,才能看到一个新的页面
  1. 客户端渲染

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sR3NaEyK-1627096149843)(./img/1626856803366.jpg)]

  • 前端 更为独立,不再受限制于 后端
  • 不足之处
    • 首屏渲染慢
    • 不利于SEO
  1. 为什么客户端渲染首屏渲染慢
  • 服务端渲染页面直出
  • 客户端渲染先加载模版页,再加载js,最后通过ajax加载数据。3批串行的网络请求,如果客户端网络不好,更加显慢。
  1. 为什么客户端渲染不利于 SEO
  • SEO 搜索引擎排名,搜索引擎会去爬取网站进行分析,收录
  • 搜索引擎不是浏览器,不会去执行js,拿到的只是html静态页面(当成字符串解析)
  • 单页面程序seo基本为0
  1. 现代化的服务端渲染 同构渲染
  • 同构渲染 = 后端渲染 + 前端渲染
  • 基于 React、 Vue 等框架,客户端渲染和服务器渲染的结合
    • 在服务器端执行一次,用于实现服务器端渲染(首屏直出)
    • 在客户端再执行一次,用于接管页面交互
  • 核心解决SEO和首屏渲染慢的问题
  • 拥有传统服务端渲染的优点,也有客户端渲染的优点
  • 如何实现同构渲染
    • 使用 Vue、React等框架的官方解决方案
      • 优点:有助于理解原理
      • 缺点:需要搭建环境,比较麻烦
    • 使用第三方解决方案
      • React 生态的 Next.js
      • Vue 生态的 Nuxt.js
  1. 通过 Nuxt 体验同构渲染
  • yarn add nuxt
  • “scripts”: { “dev”: “nuxt” }
  • asyncData
  • 首屏是通过服务端渲染出来的
  1. 同构渲染的 SPA 应用

  2. 同构渲染的问题

  • 开发条件受限
    • 浏览器特定的代码只能在某些生命周期钩子函数中使用
    • 一些外部扩展库可能需要特殊处理才能在服务端渲染应用中运行
    • 不能在服务端渲染期间操作DOM
    • 某些代码操作需要区分运行环境
  • 涉及构建和部署的要求更多
    •           构建                 部署
      
    • 客户端渲染 仅构建客户端应用即可 可以部署在任意web服务器中
    • 同构渲染 需要构建两个端 只能部署在 Node.js Server
  • 更多的服务端负载
    • 在 Node 中渲染完整的应用程序,相比仅仅提供静态文件的服务器 需要大量占用CPU资源
    • 如果应用在高流量环境下使用,需要准备相应的服务器负载
    • 需要更多的服务端渲染优化工作处理
  • 服务端渲染使用建议
    • 首屏渲染速度是否真的重要?
    • 是否真的需要SEO?
任务三:NuxtJS基础
  1. NuxtJS介绍
  • 一个基于Vue.js生态的第三方开源服务端渲染应用框架
  • 它可以帮我们轻松的使用Vue.js技术栈构建同构应用
  • https://github.com/nuxt/nuxt.js
  1. 初始化NuxtJS项目
  • Nuxt.js的使用方式
    • 初始项目
    • 已有的Node.js服务端项目
      • 直接把Nuxt当作一个中间件集成到 Node Web Server 中
    • 现有的Vue.js项目
      • 非常熟悉 nuxt.js
      • 至少百分之10的代码改动
  • 官方文档 [https://zh.nuxtjs.org/docs/2.x/get-started/installation]
  • 根据官方文档
    • 方式一:使用 create-nuxt-app
    • 方式二:手动创建
  1. 案例代码分支说明
  • git branch 分支名称 创建分支
  • git branch 查看所有分支
  • git checkout 分支名称 切换分支
  1. 路由-基本路由

  2. 路由-路由导航

  • a 标签
    • 它会刷新整个页面,不用使用
  • nuxt-link 组件
  • 编程式导航 通过js跳转
  • 参考 router-link 就行了,基本一致 [https://router.vuejs.org]
  1. 路由-动态路由
  • _id.vue 动态路由,对应 vue-router /:id
  1. 路由-嵌套路由
  • router-link: template占位,children 配置嵌套路由地址
  • nuxt: users.vue 文件中占位,同名users/index.vue 文件夹出现的文件就是子路由文件
  1. 路由-自定义路由配置
  • nuxt.config.js 中配置路由规则
  1. 视图-模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-57euJ1gT-1627096149844)(./img/1626875411461.jpg)]

  • 最外层的 html 模版是看不到的,可以在src文件夹(默认项目根目录下)下创建 app.html
 <!-- 默认模版 -->
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
  <head {{ HEAD_ATTRS }}>
    {{ HEAD }}
  </head>
  <body {{ BODY_ATTRS }}>
      <!-- 渲染的内容最终会注入到这里 -->
    {{ APP }}
  </body>
</html>
  1. 视图-布局
  • layouts/default.vue 第二层所有组件的默认父路由,默认布局组件
  • 页面出口,类似于子路由出口
  1. 异步数据-asyncData
  • 基本用法
    • 它会将asyncData返回的数据融合组件data方法返回数据一并给组件
    • 调用时机:服务端渲染期间和客户端路由更新之前
  • 注意事项
    • 只能在页面组件中使用
    • 没有this,因为它是在组件初始化之前被调用的
  • asyncdata 当想要动态页面内容有利于SEO或者是提升首屏渲染速度的时候,就在asyncData 中发请求拿数据
  • data 如果是非异步数据或者普通数据,则正常初始化到 data中即可
  1. 异步数据-上下文对象
  • asyncDate(context)
任务四:NuxtJS综合案例
  1. 案例介绍
  • 案例名称:realworld
  • 一个开源的学习项目,目的就是帮助开发者快速学习新技能
  • GitHub 仓库: https://github.com/gothinkster/realworld
  • 在线示例: https://demo.realworld.io/#/
  • 模版地址 https://github.com/gothinkster/realworld-starter-kit/blob/master/FRONTEND_INSTRUCTIONS.md
  • API 地址 https://github.com/gothinkster/realworld/tree/master/api
  • 学习收获
    • 掌握使用Nuxt.js开发同构渲染应用
    • 增强Vue.js实践能力
    • 掌握同构渲染应用中常见的功能处理
      • 用户状态管理
      • 页面访问权限处理
      • SEO 优化
    • 掌握同构渲染应用的发布于部署
  1. 项目初始化-创建项目
  • mkdir realworld-nuxtjs
  • npm init -y
  • npm i nuxt
  • 配置启动脚本 “scripts”: { “dev”: “nuxt” }
  • 创建pages目录,配置初始页面
  1. 项目初始化-导入样式资源
  • 导入样式资源
    • 模版地址 中的头部三个css文件
    • 创建 app.html 导入nuxt模版,引用这三个css文件
    • https://www.jsdelivr.com 这个可以找到资源在国内的cdn地址 如:ionicons
    • 第二个css文件,googleapis已经在国内有资源,可以不处理
    • 第三个css,直接下载下来,放在static 文件夹下。第一个css不这样做,是因为引用了一些字体,处理起来比较麻烦
  • 配置布局组件
  • 配置页面组件
  1. 项目初始化-布局组件
  • pages/layout/index.vue
    • 把模版的顶部和底部复制过来,中间放子路由
  • nuxt.config.js
    module.exports = {
        router: {
            // 自定义路由表规则
            extendRoutes(routes, resolve) {
                // 清除Nuxt.js基于pages目录默认生成的路由表规则
                routes.splice(0)
                routes.push(...[
                    path: '/',
                    component: resolve(__dirname, 'pages/layout/'),
                    children: [
                        {
                            path: '', // 默认子路由
                            name: 'home',
                            component: resolve(__dirname, 'pages/home'),
                        }
                    ]
                ])
            }
        }
    }
    
  1. 项目初始化-导入登录注册页面
  • login/index.vue
  • 配置 Login/Register url
export default {
  computed: {
      // 处理 Login/Register 页面的不同
      isLogin() {
          return this.$route.name === 'login'
      }
  }
}
  1. 项目初始化-导入剩余页面

  2. 项目初始化-处理顶部导航链接

  3. 项目初始化-处理导航链接高亮

  • 配置在router中 linkActiveClass: ‘active’
  • 在nuxt-link 标签上写 exact 表示精确匹配(url)
  1. 项目初始化-封装请求模块
  • npm i axios
  1. 登录注册-实现基本登录功能
  • @submit.prevent = “” 表单阻止默认事件(只触发注册的事件)
  1. 登录注册-封装请求方法

  2. 登录注册-表单验证

  • 给input 标签加上 required 属性,原生的表单验证
  • 邮箱 type改成 email就会验证
  1. 登录注册-错误处理

  2. 登录注册-用户注册

  • minlength=8
  1. 登录注册-解析存储登录状态实现流程

  2. 登录注册-将登录状态存储到容器中

  3. 登录注册-登录状态持久化

  • npm i js-cookie
// 仅在客户端加载 js-cookie 包
const Cookie = process.client ? require('js-cookie') : undefined
// store/index.js
const cookieparser = process.server ? require('cookieparser') : undefined
// 在服务端渲染期间运行都是同一个示例
// 为了防止数据冲突,务必要把state 定义成一个函数,返回数据对象
export const state = () => {
    return {
        // 当前登陆用户的登陆状态
        user: null
    }
}
export const mutations = {
    setUser(state, data) {
        state.user = data
    }
}
export const actions = {
    // nuxtServerInit 是一个特殊的action 方法
    // 这个 action 会在服务端渲染期间自动调用
    // 作用:初始化容器数据,传递数据给客户端使用
    nuxtServerInit ({ commit }, { req }) {
        let user = null
        if (req.headers.cookie) {
          const parsed = cookieparser.parse(req.headers.cookie)
          try {
              user = JSON.parse(parsed.user)
          } catch (error) {}
        }
        commit('setUser', user)
    }
}
  1. 登录注册-处理导航栏链接展示状态

  2. 登录注册-处理页面访问权限

  • middleware/auth.js
export default function ({store, redirect}) {
  if (!store.state.user) {
    return redirect('/login')
  }
}
// 在路由匹配组件渲染之前会先执行中间件处理
// middleware: 'auth'
  1. 首页-业务介绍

  2. 首页-展示公共文章列表

  3. 首页-列表分页-分页参数的使用

  4. 首页-列表分页-页码处理

  • watchQuery: [‘page’]
  1. 首页-列表分页-页码处理

  2. 首页-展示文章标签列表

  3. 首页-优化并行异步任务

  • Promise.all
  1. 首页-处理标签列表链接和数据

  2. 首页-处理导航栏-业务介绍

  3. 首页-处理导航栏-展示状态处理

  4. 首页-处理导航栏-标签高亮及链接

  5. 首页-处理导航栏-展示用户关注的文章列表

  6. 首页-统一设置用户Token

  • plugins/request.js
  • nuxt.config.js plugins: [’~/plugins/request.js’] 注册插件
import axios from "axios";
// 按需导出,因为默认导出被 plugin 占据了
export const request = axios.create({
    baseURL: 'https://conduit.productionready.io/'
})
export default ({ store }) => {
    // 添加请求拦截器
  request.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    let { user } = store.state
    if (user && user.token) config.headers.Authorization = `Token ${user.token}`
    // 返回config 配置对象
    return config;
  }, function (error) {
    // 对请求错误做些什么,这个是请求发送之前的异常
    return Promise.reject(error);
  });
}
  1. 首页-文章发布时间格式化处理
  • dayjs
  • npm install dayjs --save
  • vue.filter 注册插件
  1. 首页-文章点赞

  2. 文章详情-业务介绍

  3. 文章详情-展示基本信息

  4. 文章详情-把Markdown转为HTML

  • markdown-it
  1. 文章详情-展示文章作者相关信息

  2. 文章详情-设置页面meta优化SEO

  3. 文章评论-通过客户端渲染展示评论列表

  4. 发布部署-打包

  5. 发布部署-最简单的部署方式

  • 最简单的部署方式
    • 配置Host + Port

      • nuxt.config.js => server: {host: ‘0.0.0.0’, port: 3000}
    • 压缩发布包

      • .nuxt static nuxt.config.js package.json package-lock.json => realword-nuxtjs.zip
    • 把发布包传到服务端

      • ssh root@10.10.10.0 登陆服务器
      • mkdir realworld-nuxtjs
      • cd realworld-nuxtjs/
      • pwd
      • exit
      • scp ./realword-nuxtjs.zip root@10.10.10.0:/root/realworld-nuxtjs
      • ssh root@10.10.10.0 登陆服务器
      • cd realworld-nuxtjs/
      • unzip realword-nuxtjs.zip
      • ls -a 看隐藏文件
      • npm i
      • npm run start
    • 解压

    • 安装依赖

    • 启动服务

  1. 发布部署-使用PM2启动Node服务
- npm install --global pm2
- pm2 start 脚本路径
- pm2 start npm -- start
- pm2 stop id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FXX1ZdK9-1627096149845)(./img/1627037127513.jpg)]
  1. 发布部署-自动化部署介绍
- 现代化的部署方式(CI/CD)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HmcXSRDi-1627096149846)(./img/1627037335411.jpg)]
  1. 发布部署-准备自动部署内容
- CI/CD 服务
  + jenkins
  + Gitlab CI
  + GitHub Actions
  + Travis CI
  + Circle CI
- 环境准备
  + Linux 服务器
  + 把代码提交到GitHub 远程仓库
- 配置 GitHub Access Token
  + 生成:https://github.com/settings/tokens
  + 配置到项目的Secrets 中:https://github.com/lipengzhou/realworld-nuxtjs/settings/secrets
  + 右上角用户》Settings > Developer settings > Personal access tokens > Generate new token > repo 勾选 > 最下方 Generate token
    - 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
    - 复制令牌
    - 打开远程仓库 》 Settings 》Secrets 》 new Secrets 》Name:TOKEN (脚本中要保持一致) Value 放令牌
  + 配置GitHub Actios 执行脚本
    - 在项目根目录创建 .github/workflows 目录
    - 下载 main.yml到workflows目录中
      + https://gist.github.com/lipengzhou/b92f80142afa37aea397da47366bd872
    - 修改配置
    - 配置PM2 配置文件
      + pm2.config.json { "apps": [ { "name": "RealWorld", "script": "npm", "args": "start" } ] }
      + 需要把 pm2.config.json加入到 main.yml 打包构建的文件中去
      + 配置服务器信息,打开远程仓库:Settings 》Secrets 》 new Secrets 》
        - Name:USERNAME Value:root
        - Name:PORT Value:22
        - Name:HOST Value: 100.100.99.98
        - Name: PASSWORD Value:
    - 提交更新
    - 查看自动部署状态
    - 访问网站
    - 提交更新
  1. 发布部署-自动部署完成
  • git tag v0.1.0 在main.yml中以v开头才会集成
  • git tag
  • git push origin v0.1.0
  • 查看远程仓库 Actions 构建这一栏
  • Release 可以查看到构建完成的发布包
  • git add . gti commit ‘’ git push ,只是推送了本地的历史记录
  • git tag v0.1.1 只有生成一个新的标签并且推送才回构建
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧农业是一种结合了现代信息技术,包括物联网、大数据、云计算等,对农业生产过程进行智能化管理和监控的新模式。它通过各种传感器和设备采集农业生产中的关键数据,如大气、土壤和水质参数,以及生物生长状态等,实现远程诊断和精准调控。智慧农业的核心价值在于提高农业生产效率,保障食品安全,实现资源的可持续利用,并为农业产业的转型升级提供支持。 智慧农业的实现依赖于多个子系统,包括但不限于设施蔬菜精细化种植管理系统、农业技术资料库、数据采集系统、防伪防串货系统、食品安全与质量追溯系统、应急追溯系统、灾情疫情防控系统、农业工作管理系统、远程诊断系统、监控中心、环境监测系统、智能环境控制系统等。这些系统共同构成了一个综合的信息管理和服务平台,使得农业生产者能够基于数据做出更加科学的决策。 数据采集是智慧农业的基础。通过手工录入、传感器自动采集、移动端录入、条码/RFID扫描录入、拍照录入以及GPS和遥感技术等多种方式,智慧农业系统能够全面收集农业生产过程中的各种数据。这些数据不仅包括环境参数,还涵盖了生长状态、加工保存、检验检疫等环节,为农业生产提供了全面的数据支持。 智慧农业的应用前景广阔,它不仅能够提升农业生产的管理水平,还能够通过各种应用系统,如库房管理、无公害监控、物资管理、成本控制等,为农业生产者提供全面的服务。此外,智慧农业还能够支持政府监管,通过发病报告、投入品报告、死亡报告等,加强农业产品的安全管理和质量控制。 面对智慧农业的建设和发展,存在一些挑战,如投资成本高、生产过程标准化难度大、数据采集和监测的技术难题等。为了克服这些挑战,需要政府、企业和相关机构的共同努力,通过政策支持、技术创新和教育培训等手段,推动智慧农业的健康发展。智慧农业的建设需要明确建设目的,选择合适的系统模块,并制定合理的设备布署方案,以实现农业生产的智能化、精准化和高效化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值