Ionic 很棒且易于学习,特别是如果您有使用其他框架和库(如 react、angular 或 vue)的经验,我的意思是说,我的意思是对 Ionic 有一点经验是“成功”遵循的先决条件,尽管我会尝试尽我所能解释一切。

我会将这些文章分成几部分,希望到最后我们将拥有一个可以工作的混合移动应用程序和一个网站/网络应用程序,它们都与 supabase 交互。

我们将创建一个模板编辑器(某种形式的 IDE,用于创建和管理时事通讯、电子邮件之类的模板)和一个移动应用程序来“可视化”模板。通常时事通讯是通过电子邮件发送的,并且看起来设计和东西,其中大部分。我们要做的是删除电子邮件,因为中间人创建一个编辑器和一个移动应用程序来将模板“解释”为 HTML 并为用户呈现它们。

为了解释标题中的“很好”,我创建这个应用程序的原因是向某个组织推销它以获得积分甚至工作机会,我注意到他们没有移动应用程序,他们也发送像每天一样发布时事通讯和电子邮件,所以这个应用程序可能会让他们的生活更轻松。所以市场不能是 google play,但是有很好的教程涵盖了这个主题,我们将构建一个签名的 apk 等,但不发布它。

计划:

首先是我们可能会在 netlify 上托管的编辑器(用于管理员):

功能:创建时事通讯模板,保存草稿,发布到 supabase。

你也可以申请这个特定组织的工作,他们通过电子邮件回复每个人,我们也可以在编辑器旁边添加一个工作板,这样用户就可以通过移动应用程序申请,也可以得到回复,因为这是我们真正做的原型不需要身份验证,但我们会看到。

移动应用程序(用户):

特点:从 supabase 中提取新的时事通讯(可能添加评论部分),检查工作申请状态。

我们将两者分开,这有点奇怪,因为它打败了离子的原因:这是一个代码库所有平台的东西,但是我有一个理由,通过构建来学习一个工具,我想为什么不学习 vue和 supabase 这样做的时候,因为这可能会走向南方,所以为什么不拿出一些新的东西呢,而且我们都将学习如何通过构建来学习,我也知道你可以将 vue 与 ionic 一起使用,所以为什么要将它们分开,好吧,我很难学到这一点:

我曾经和我的几个朋友一起构建了一个具有 ionic 和 angular 的移动应用程序,名为 Money Manager,发布了它,具有许多功能,作为我们的第一个完整应用程序,此时我认为我非常了解 angular 将它放在我的linkedin 描述中,长话短说被要求展示我的角度技能,不用说这是一场灾难,纯粹的角度与离子轮的角度相比感觉有很大不同,因此学习一个纯粹形式的框架是学习它的最好方法,因为离子带来了它自己的东西,并且框架在某种程度上被 ionic 增强了。

另一个原因:我们将使用带有很多插件的Editorjs模块,而移动应用程序根本不需要它们,因此将两者结合起来会使apk变大,编辑器和移动应用程序之间绝对没有重叠。话虽如此,让我们开始吧。

快速 Vue 回顾/介绍

我假设您知道组件是什么,也许从使用 react 开始,我不会解释组件是什么,如果我们从头开始,这些文章会很长,所以我建议您在继续之前:拥有基本的所提到的知识,或者只是反应的基础知识,这将是很好的,因为我们将在移动应用程序中使用反应,所以简而言之,如果你是一个绝对的初学者,这可能会超过你的头脑,所以我建议采取基本的反应图.

.vue 文件


<template>
     <div>
        <button @click="sayHello"></button>
     </div>

</template>

<script>
 // I will refer to the below {} as exported object
  export default {
    name: "Hello",
    props: {
     msg: String
    },
    methods: {
       sayHello: function() {
          console.log(`hello ${this.msg}`)

       }
     }
  }

</script>

<style scoped>


</style>

进入全屏模式 退出全屏模式

我们的标记放在模板标签中(只是普通的 HTML 标记),我们的 JavaScript 和它们各自标签中的样式也是如此

模板标签 - Vue 像任何其他框架/库一样具有“它自己的”绑定和指令样式,例如使用 @click 将点击事件绑定到按钮,使用 v-bind 创建受控元素(绑定到值的元素),条件使用 v-if 等渲染,没有什么真正神奇的,特别是如果你来自反应世界或使用 ng 指令的角度。

脚本标签 - 与组件相关的逻辑、功能和数据位于导出的对象中。方法、道具、组件名称、数据等都在这个对象中定义,也许有一些变化,但据我所知,大多数事情都发生在这个对象中,我们可以涵盖它们,但因为这篇文章不是关于 vue,相反,我们将介绍相关的内容,就导入而言,它们在导出的对象之外。

name - 用于在导出组件时标识组件

props - 是一个具有在使用组件时必须传递的属性的对象,并且似乎在 vue 文件中允许使用类型,尽管使用的是 JavaScript,到目前为止,在 say hello 方法中看到的任何方法中都可以直接访问 props 而无需this.msg

方法 - 模板可以访问此处定义的函数,以访问您使用的对象内的任何属性

this

像往常一样的关键字(指您所在的当前对象)

样式标签 - scoped 表示样式只限于当前组件,不会应用到组件之外,

导入组件

记住导出对象中的 name 属性,它是组件的标识符,要将任何组件导入其他组件,我们使用导出对象中为组件指定的名称,例如

   import Hello from "./components/Editor.vue"; // in the script tag 


   // then we can use Hello as a component in the template
   //msg here is the prop we difined in the Hello component

   <Hello msg="world"/> // template

进入全屏模式 退出全屏模式

生成的应用程序(使用@vue/cli)

现在我认为我们可以轻松地跟随生成的应用程序的流程,当我接近一个新的框架或模块时,我总是在 vue 的 main.js 中寻找入口点并跟随流程。看起来像这样:

文件:main.js

 // createApp mounts a component to an HTML element (in index.html)
import { createApp } from "vue";

// importing App component from app.vue
import App from "./App.vue";


// mounting App component(or generated element(s) to an element on our index.html with the id app) which is a div in this case
// simply mounting app component to the div in index.html
createApp(App).mount("#app");


进入全屏模式 退出全屏模式

文件:App.vue(在 main.js 中导入)

基本上这是挂载在 main.js 中的入口组件,根组件

 <template>



 <Ed msg="Editor" />

</template>



<script>

// custom component for the Editor we will create
import Ed from "./components/Editor.vue";



export default {

 name: "App",

 components: {

 Ed,

 },

};

</script>



<style>

#app {

 font-family: Avenir, Helvetica, Arial, sans-serif;

 -webkit-font-smoothing: antialiased;

 -moz-osx-font-smoothing: grayscale;

 /* text-align: center; */

 color: #2c3e50;

 margin-top: 60px;



}

</style>


进入全屏模式 退出全屏模式

我稍微改了一下,导入了我们自定义的 Ed 组件,也就是 Editor,你可以在组件属性中拥有尽可能多的组件,然后你可以在模板中使用,现在我们只有一个,Editor 组件,姓名:埃德

文件:Editor.vue(在 components 文件夹中创建此文件)

我们将离开 msg 道具,作为早期使用道具的借口,以适应它们。

文件:Editor.vue


 <template>

// div to append the editor later on
 <div id="editor">
    <!-- usage of the prop -->
    h1>{{ msg }}</h1>

 </div>

</template>

<script>
    export default {

     name: "Ed",
     //props the component receives
     props: {

     msg: String,

     },

     }

</script>

<style scoped>


</style>


进入全屏模式 退出全屏模式

现在我们开始,我们将首先处理编辑器,然后是预览窗口。当我从事个人项目时,我通常遵循一个简单的模式,首先是功能,最后是设计,因为很容易迷失在设计细节中,所以我们的目标是得到一个工作原型。

编码编辑器

Editorjs 简单、可扩展、功能强大的同时,我们也会开发自己的插件。我们在 editor.vue 文件中,上面的代码片段我就不展示了,....


 <script>
  import Editorjs from "@editorjs/editorjs"

 export default {
 ....

 methods: {
    initEd: function(){
        /**
         * @type {EditorJS}
         */

         window.editor = new Editorjs({

             holder: "editor",
             onready: function(){

                 console.log(window.editor)

             }

         })

      }

   }
 }

 </script>

进入全屏模式 退出全屏模式

首先,我们导入之前在插件中安装的 Editorjs,然后在方法中的“导出对象”中定义一个 initEd 方法——初始化编辑器,

如果您不使用 JavaScript 或 IDE,则可以忽略此行:

         /**
         * @type {EditorJS}
         */

进入全屏模式 退出全屏模式

这告诉 Visual Studio 代码以下变量的类型,在我们的例子中是 window.editor,这是用于代码完成的。

这里我们正在初始化一个新的编辑器对象并将其存储在全局窗口对象中(您也可以将其存储为导出对象的属性,因此您只能在该范围内访问它)

  window.editor = new Editor()

进入全屏模式 退出全屏模式

Editor() 将对象作为参数,我们在其中为新创建的编辑器传递选项或配置

    Editor({

     holder: "editor",   // Id of the HTLM element to append the created editor
     onready: function(){
          // called when the Editor is ready
     }


    })


进入全屏模式 退出全屏模式

我们已经对编辑器进行了简单的设置,但是我们需要对其进行初始化,在 initEd 方法下,让我们创建一个新的函数 mount 来调用 initEd 函数,我们当然可以直接调用 initEd 但我们正在计划未来,让我们假设有一天我们想在初始化编辑器之前做一些事情,我们可以将该逻辑放入 mount 中,与实际设置编辑器的 initEd 逻辑分开,将功能分离到分离方法是一种很好的做法

methods: {
    initEd: function(){
     .....
    }, 

    mount: function() {
       this.initEd(); // calling initEd
     }

}

进入全屏模式 退出全屏模式

Vue 也有生命周期方法,这些方法是根据组件状态触发的,例如当组件完成挂载时,我们将使用它来挂载我们的编辑器。

Mounted 是其中一种循环方法,将其放在我们的方法之外并调用 mount


export dafault {
methods: {
 .....
}, 

mounted(){
  this.mount()
 }
}

进入全屏模式 退出全屏模式

现在我们有了编辑器,如果你打开控制台,你会看到一条消息说编辑器准备好了,如果你点击带有 id 编辑器的 div 上的任意位置,编辑器将聚焦。如果您知道 jupyter notebooks,编辑器就是这样,但用于编辑 HTML,因此我们可以创建模板。

看到编辑器有点困难,因为一切都是白色的,让我们更改页面的背景或只是我将做的编辑器支架的边框,您可以将其更改为任何颜色,它是临时的,所以我们可以看到我们的正在与

在 Editor.vue 样式标签中:

#editor{

     width: 80%;  // width of the editor

     border: solid 3px lightblue;

}

进入全屏模式 退出全屏模式

在模板标签中,将编辑器持有人放在带有类容器的 div 中


<div class="container">

     <div id="editor"> 

     <!-- <h1>{{ msg }}</h1> --> // the msg prop commented out we do need it yet

     </div>

</div>

进入全屏模式 退出全屏模式

居中编辑器


.container {

     display: flex;

     justify-content: center;



}

进入全屏模式 退出全屏模式

完成此操作后,您应该能够看到带有您选择的边框颜色的编辑器

当您单击编辑器时,您应该会在左侧看到一个加号图标,在另一侧看到一个方形图标,加号图标用于选择工具,如果您想到任何您知道的编辑器,比如 gimp、krita 等,他们有绘图工具,选择一个工具决定了编辑器的行为,把它想象成一个 HTML 元素,它决定了活动块中的内容(我们会设置工具不用担心)

活动块表示在加号和方形图标之间的选定块

快速提示:如果您在更改代码时没有在编辑器中看到自动更改,刷新,我认为这与挂载方法的调用方式以及更改后的刷新有关!

当您单击加号图标时,将出现此类工具栏(我们将在接下来设置),如您所见,我们有文本工具(用于编写文本)、标题、表格等,您选择的工具将决定当前块(加号和方形图标之间)将是(表格,标题等)

[截图 (881)](https://res.cloudinary.com/practicaldev/image/fetch/s--_4VJ3Y0X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/bdd5hy969bmrzc3345uw.png)

选择工具后,方形图标用于对所选工具的块进行变化,假设您选择了标题工具,单击方形将显示从 H1 到 6 的所有可用标题类型,这个方形图标也用于移动如果有很多块,则向上或向下块,或者删除一个块。

现在对编辑器已经足够了,让我们设置工具,创建一个名为实用程序的新文件夹,并在其中创建一个文件 Tool.js | ts 如果您选择 typescript(对于 typescript,您必须创建 env.d.ts 文件,我相信并声明我们将要导入的所有工具,因为它们没有类型)

工具

import Checklist from "@editorjs/checklist"

import Delimeter from "@editorjs/delimiter"

import Header from "@editorjs/header"

import List from "@editorjs/list"

import Marker from "@editorjs/marker"

import Quote from "@editorjs/quote"

import Raw from "@editorjs/raw"

import Table from "@editorjs/table"

import Warning from "@editorjs/warning"



export const tools = {

 Checklist,

 Delimeter, 

 Header, 

 List, 

 Marker,

 Quote, 

 Raw, 

 Table, 

 Warning

}




进入全屏模式 退出全屏模式

在script标签中导入Editor.vue文件中的工具

<script>

import {tools} from "../utillity/Tools"

export default {
 ...
}


<script>



进入全屏模式 退出全屏模式

然后将工具连接到设置对象中的编辑器,就在持有人之后添加这一行


 window.editor = new EditorJs({
   ...
   tools: tools,
   ...

 })

进入全屏模式 退出全屏模式

如果您刷新并使用编辑器,现在您将看到所有工具都已连接并正在工作,稍微玩一下并习惯它,因为稍后我们将创建自己的工具,还有通过 npm 提供的很棒的工具

好的,至少对于第 1 部分,让我们添加一种保存模板(编辑器)数据的方法,

数据是表示模板顺序、元素类型、它们的数据等的对象

我们将在移动应用程序中使用这些数据来重建模板,当您真正考虑它时,它是一种非常有效的传输视觉数据的方法:与传输实际组件相比,查看大小。

添加一个按钮,无需对其进行样式设置,以保存(控制台)来自编辑器的数据


<template>
<div class="container">

 <button @click="save">Save</button>

 </div>

 ...
</template>


进入全屏模式 退出全屏模式

还向容器类添加填充


.container{
...
padding: 1em;

}

进入全屏模式 退出全屏模式

在方法对象内部添加保存方法


methods: {
 ...,
  save: function(){

         window.editor.save().then((data)=> {

            console.log(data, "saved succesfully")

         })

 },

}



进入全屏模式 退出全屏模式

我们调用编辑器保存方法,它返回一个承诺,在解析时返回我们在回调中传递的块数据

用虚拟数据填充编辑器,点击保存,在控制台中你会看到编辑器表示为一个对象,这是我们要传递的数据,重建我们的UI,看看它,很容易理解,我们对 blocks 属性更感兴趣

[屏幕截图 (883)](https://res.cloudinary.com/practicaldev/image/fetch/s--tqjHHWpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/s9p8j93lduyrpmmdu2fg.png)

下一个

1.我们将创建我们的自定义插件一个图像工具

  1. 一个简单的 express 本地服务器,用于在我们的编辑器和 ionic 应用程序之间建立通信(用于快速开发和测试) - 服务器将传递块数据

3.设置离子应用

问题或想打个招呼,最好的方法是推特:

推特网址

第 2 部分

自定义插件

图片插件

在 src 目录下新建一个 plugins 文件夹,在 plugins 里面创建 image.js || ts 取决于您使用的是什么,还创建 image.css


src/
  plugins/
    image.js || image.ts 
    image.css



进入全屏模式 退出全屏模式

在 image.js 中:

导入css文件,我们还需要一个工具图标,我们将使用开源图像图标(svg)

import "./image.css";

const icon = 

`<svg version="1" xmlns="http://www.w3.org/2000/svg" width="17" height="15" viewBox="0 0 48 48" enable-background="new 0 0 48 48">

<path d="M40,41H8c-2.2,0-4-1.8-4-4V11c0-2.2,1.8-4,4-4h32c2.2,0,4,1.8,4,4v26C44,39.2,42.2,41,40,41z"/>

<circle fill="#fff" cx="35" cy="16" r="3"/>

<polygon fill="#000" points="20,16 9,32 31,32"/>

<polygon fill="#eee" points="31,22 23,32 39,32"/>

</svg>


进入全屏模式 退出全屏模式

我们可以直接在我们的代码文件中导入 css,因为代码会被转译,并且一些转译器理解 css 导入并能够使用它。

现在我们完成了设置,创建了一个导出的类(我们将在我们的 tools.js 中导入它)


export default class Image{
  static get toolbox(){

         return {

         name: "Image",

         icon

         }

  }

}

进入全屏模式 退出全屏模式

静态方法是类的方法,可以在不实例化类的情况下访问和调用,并且不能在实例中访问,只能通过类名访问工具箱:

Image.toolbox()   // static method

进入全屏模式 退出全屏模式

这个方法被编辑器调用来获取工具的信息,可以看到工具箱返回一个对象,带有我们自定义图像工具的名称和图标,它将在编辑器中渲染,让我们连接我们的图像工具,注意你可以根据自己的喜好更改 svg 中的填充,我选择了随机颜色,稍后我们将调整 svg 的大小,

在工具文件中:

...
import Image from "../plugins/Image"

export const tools{
...,
Image


}



进入全屏模式 退出全屏模式

我们正在导入 Image 工具并将其添加到 const 工具中,如果您刷新编辑器并打开工具,您将看到一个额外的工具,如果您单击它,则不会发生任何事情,因为我们没有告诉编辑器如何处理它点击,我们只是告诉了它,基本上如何在工具部分而不是在块中渲染它,为此我们需要一个渲染方法,让我们回到我们的插件文件并添加一个渲染方法,让我们渲染经典的hello world,注意构造函数在对象创建时运行(执行类)


export default Image{

   div = document.createElement("div")

 /**

 * @type {HTMLHeadingElement}

 */

  h1 = document.createElement("h1")


  constructor(){
       this.h1.innerText = "Hello World"

       this.div.appendChild(this.h1)

  }

  static get toolbox{
   ...
  }

  //new 

      render(){
       return this.div;
      }

}



进入全屏模式 退出全屏模式

我相信这是不言自明的,我们创建一个 div 和 h1 元素并将它们分配给类的全局变量,在构造函数中我们将 hello world 的文本分配给 h1 元素并将 h1 附加到 div 元素,最重要的是我们在classes render方法中返回div,这告诉编辑器在单击图像工具时要渲染什么,在本例中为hello world的h1的div,如果您现在测试该工具,您将看到hello world显示在块中

如果您尝试保存,您会看到一个错误,因为我们没有告诉编辑器如何处理“键入我们的工具”块,我们必须定义保存时要做什么

export default Image{
...
constructor(){
...
}

 static get toolbox{
   ...
  }


 render(){
     ...
    }



 save(blockContent){

     let h1 = blockContent.firstElementChild;

     // data to be saved on  saving the editor data
     return {

        text: h1.innerText

      }

  }
}


进入全屏模式 退出全屏模式

在 save 方法中,我们有 blockContent 的参数,它等于在 render 方法中返回的元素,在本例中是 div 元素,我们访问 div 的第一个子元素并返回一个带有 h1 文本的对象(这是我们的数据)和文本的键,这个对象将保存在块数据中


{
    "time": 1628364784438,
    "blocks": [
        {
            "id": "6eiD2XG4CS",
            "type": "Image",
            "data": {
                "text": "Hello World"
            }
        }
    ],
    "version": "2.22.2"
}

进入全屏模式 退出全屏模式

很简单,因为我们有一个完整的工作工具,你可以用这个简单的想法做很多事情,你可以创建一个完整的网页并在渲染方法中返回它,现在我们已经有了创建自定义工具的基础,清除所有内容并离开相关方法(构造函数、工具箱、渲染和保存),创建图像工具,我假设您现在已经了解了工具创建的基础知识,所以我会更快一点(意思是更少的解释)我们只是重复上述过程但是创建图像元素。

图像工具

创建元素和设置公共(类变量)


export default class Image{

// to construct the image holder
 div = document.createElement("div")

 input = document.createElement("input");

 img = document.createElement("img")

 label = document.createElement("label");

 paragraph = document.createElement("p");



// handle data 
 data = undefined;

 file = undefined;

 base64Img = undefined


 constructor(data){

  }

}




进入全屏模式 退出全屏模式

首先我们将使用输入类型文件来选择图像,我们将只支持本地图像选择(主要是为了避免可能会影响用户的CORS策略),您可以实现图像的在线检索,现在我们将使用输入类型文件,

构造函数中的数据参数,在初始化之前将数据传递给编辑器时使用,对于编辑等,我们将在接下来的部分中暂时忽略它。


export default Image{
...

constructor(data){

  // adding classes to the elements(classes will be difined in the imported css)
  this.div.classList.add('img__container')

 this.input.classList.add("file")

 this.paragraph.classList.add("file-name")


   // making the input of type file 
  this.input.type = "file";



     // label for file input

     this.label.htmlFor = "file";

     this.label.textContent = "Select Image";

}



}



进入全屏模式 退出全屏模式

构建元素结构(将各个部分放在 div 中)

export default Image{
...

constructor(data){
...


 this.div.appendChild(this.input)

 this.div.appendChild(this.label)

 this.div.appendChild(this.img)

 // binding a click event to the label(Select Image) on click it will trigger
 // and open file selector (for img selction)

  this.label.onclick = () => {

      this.input.click();

    }


// handling file select
 this.input.onchange = (event)=> {


 }

}


}


进入全屏模式 退出全屏模式

关于文件更改:


export default Image{
...

constructor(data){
...

 this.input.onchange = (event)=> {

    // getting the selected file from the input element
   const file = this.input.files[0];


   // creating a file reader object(we are using a file reader so it will turn the //image to base64 for easy storage and transport)

   let reader = new FileReader()

   // read data as a blob 
   reader.readAsDataURL(this.input.files[0])

  // when the reader finishes reading the file 
  // asign the result to img.src and public base64img var for use in saving stage
    reader.onloadend = (ev) => {

        // console.log(typeof ev.target.result)

         this.img.src = ev.target.result

         this.base64Img = ev.target.result

    }



 }
}



进入全屏模式 退出全屏模式

更新 render() 以返回正确的 div 和 save() 也


export default class Image{
...
constructor(){
...
}

 static get toolbox(){
 ...
 }



 render(){

// the div which we appended the img element
 return this.div

 }

 save(blockContent){

      // saving the base64 
     return {

     img: this.base64Img

     }

 }


}



进入全屏模式 退出全屏模式

如果您现在测试图像工具,它应该能够选择图像并保存,但是还没有样式,让我们添加它们

设置文件元素的样式:文件元素很难设置样式,我将要给你的 css 我很久以前从 stack-overflow 学到的,我不太记得那个帖子了,如果我记得我会参考。请注意,我不会解释 css,如果我开始这样做,这些帖子会很长,正如您已经注意到的那样,我忍不住要解释所有内容,所以我只会给您 css,也许在未来我可以做一个中间 css thingy 或 post

所以打开 image.css 文件:


input{

 width: 100%;

 border: 3px solid aquamarine;

}



// hiding the ugly native file element
.file {

opacity: 0;

width: 0.1px;

height: 0.1px;

position: absolute;

}

// i chose flex dir column(for adding elements later under the img, maybe captions etc)

.img__container{

 display: flex;

 flex-direction: column;

 align-items: center;


 margin: 1em 0;

}


//making sure the img is confined within the parent element
.img__container img {

 width: 100%;

 height: auto;

}

// the custom (Select Image) label that triggers file select


.img__container label{

     display: block;

     position: relative;

     width: 200px;

     height: 50px;

     border-radius: 25px;

     background: linear-gradient(40deg, #ff6ec4,#7873f5);

     box-shadow: 0 4px 7px rgba(0, 0,0, 0.4);

     display: flex;

     align-items: center;

     justify-content: center;

     color: #fff;

     font-weight: bold;

     cursor: pointer;

     transition:  transform .2s ease-out;



     padding: .5em;

     margin: .4em;
}


// moving stuff around 
input:hover + label,

input:focus + label

 {

 transform: scale(1.02);

}



input:focus + label {

 outline: 1px solid #000;

 outline: -webkit-focus-ring-color auto 2px;

}

.file-name {

 position: absolute;

 bottom: -50px;

 left: 10px;

 font-size: 0.85rem;

 color: #555;

}



img {

 margin-top: 50px;

}

进入全屏模式 退出全屏模式

这就是css的全部内容,select上的工具现在应该出现一个选择图像按钮,选择时将打开文件对话框,图像选择时,图像将显示在按钮下,保存时,图像base64将保存在块下

至此,我们现在完成了图像工具。

让我们暂时停止编辑器并设置服务器并启动 ionic 应用程序,下一部分将致力于完成编辑器(实现模板创建、编辑、删除、草稿、发布等)

简单服务器

后端方面我也是新手,不过不用担心我会解释一切,我用express做了一点实验,很简单,除了中间件部分,包括CORS策略等,这真的是我不知道的东西我现在不想学习,所以我使用 json-server 来实现一个服务器(我从香港 uni Fron-tend Coursera 课程中学到的少数技巧之一),json-server 顾名思义,服务于 json 文件,但也允许从“头”创建服务器,好消息是:它为我们设置了中间件,它非常棒,而且是一个很好的工具,

在某处创建一个新文件夹来保存我们的服务器文件,并安装 json-server 我希望你知道如何初始化一个 npm 项目并安装包,所以我不会详细介绍

这是一个node项目:nodejs和浏览器是两个不同的环境,但是node不需要太多了解

服务器设置

创建一个 main.js 文件或索引任何您想调用的文件并导入 json-server

// using require cause it's node
 const jsonServer = require("json-server")



进入全屏模式 退出全屏模式

如果您在文章中了解了这么多,我假设您已经听说过服务器,甚至对它们是什么、路由等有基本的了解,如果没有,请熟悉一下自己,YouTube 上的 5 分钟视频就足够了。但很快,服务器充当前端应用程序的中间人,是否获取正确的数据、身份验证、通信(我们正在这样做,将我们的 Web 应用程序与 ionic 应用程序链接以传递块数据并实时测试),想想它是在两个不同世界之间建立沟通并了解它们的一种方式,它的工作是在两者之间充当经纪人,

想象一个依赖数据库的前端应用程序,并且不断地获取数据,我们可以引入一个服务器作为中间人,有逻辑,现在前端与服务器对话,服务器所做的不仅仅是获取数据。例如筛选数据并将相关数据返回到前端、清理数据、缓存数据以免对数据库征税等

设置

...


//since this is json server it can write to a json file( database) 
// which we will not do, but pass it as a param of router so you know it can 
// you can also pass an empty object to create an in memory DB instead of a json file
const router = jsonServer.router("db.json")

// middlewares involves setting cross origin, cors etc without them we cannot communicate with the server from a different ip(CROSS ORIGIN policy) for security reasons so the server must  "allow cross orgin"
const middlewares = jsonServer.defaults()

// this underneath simply creates an express app
const server = jsonServer.create()



进入全屏模式 退出全屏模式

下面的 json-server 探索起来很酷,所以我鼓励你看一下源代码,这是一个很好的学习体验,或者将来我会写一篇文章来做这件事,可能还会去 nedb,那会很酷,以任何方式查看来源。

...

// telling the server(express app to use middlewares)
server.use(middlewares)


进入全屏模式 退出全屏模式

从这里开始,您可以使用常规方法(get、put、delete 等),

我们将在执行过程中实现更多方法,现在这些就足够了


...

// get all templates 
server.get('/home', (req, res) => {

  })


// get specific template
server.get("/temp/:id", (req, res)=> {

})  

// add a template
server.post("/temp/new/:data", (req, res)=> {

} )


进入全屏模式 退出全屏模式

在路由中 "/temp/:id" :id 表示它是我们可以传递的参数

最后

server.use(router)

// important bodyParser allows use of POST, PATCH etc
// so we need to tell our server to use a bodyParser
server.use(jsonServer.bodyParser)

// i believe this is used by the db, which we will not use
// avoiding unecessary complexity, we will store our templates in a map object
server.use((req, res, next) => {
    if (req.method === 'POST') {
      req.body.createdAt = Date.now()
    }
    // Continue to JSON Server router
    next()
  })

//finally start the server and listen at port 3000
server.listen(3000, ()=> {
    console.log(`listening on port 3000`)
})



进入全屏模式 退出全屏模式

Req 和 Res 分别是处理传入请求和发送响应的对象

例如获取客户端发送的参数可以使用req.params,将消息发送回客户端,可以使用res.send

一个示例更新 /home get 方法以发回一个简单的响应:


// if the client visits this path, respond with the object
server.get("/home", (req, res)=> {
    res.jsonp({ user: 'sk' });

})



进入全屏模式 退出全屏模式

在节点中启动服务器

节点 main.js

如果你做的一切都正确,你应该看到

监听 3000 端口

在网页的 js 文件甚至 Web 控制台中:


 fetch("http://localhost:3000/home").then(res => {
          console.log(res.text().then(data => console.log(data)))
      })

进入全屏模式 退出全屏模式

您应该会看到记录的响应。

这是服务器的基本骨架,我们会在需要时回到它

离子

我假设您具有 ionic 的基本知识,但如果您不了解,那没关系,我认为 ionic 是您可以轻松掌握的框架之一,我将使用 Ionic 和 react,但是您不必这样做,但我确实推荐你这样做,所以你可以顺利进行,特别是如果你是 ionic 新手,你可以使用 angular 或 vue,我选择 react 因为我更熟悉并且想要更快地工作,vue 已经放慢了速度,就像我一样还在学习

在不同的文件夹中启动一个新的 ionic 项目,我通常使用 npx 启动我的 ionic 项目,因此我可以获得最新的更新,您可以全局安装 ionic cli 并从那里开始,它非常简单且有据可查

npx ionic start [name of your app]

进入全屏模式 退出全屏模式

系统将提示您选择首选框架和入门模板,对于入门模板,请选择 sidemenu,

如果他们问你:你想启用电容器吗?选择是,电容器是 webview 和原生 api 之间的框架或桥梁,这就是我们基本上能够使用 web 技术运行原生任务或 api 的方式

如果您在所有命令中全局安装了 ionic cli,则在 ionic 后面省略 npx

我们只会为 android 构建,但是您可以添加 ios,添加使用电容器的平台,我们不会过多关注原生平台,因为我们还不需要原生插件,我们现在将在浏览器中测试所有内容,我们现在只是建立平台,以便我们以后可以构建一个 apk。


npx ionic cap add android

进入全屏模式 退出全屏模式

cap代表电容器,你可以用同样的方式添加ios

最后运行


ionic s -l

进入全屏模式 退出全屏模式

意思是离子服务实验室

lab 是一个看起来像手机的网页,所以你基本上可以看到应用程序在手机中的样子,它不是模拟器!但是这种东西,我知道 Firefox 有这种东西,但请允许安装实验室,它更准确,并且考虑到所有手机尺寸的增加,我只在我的一个项目中尝试使用 Firefox,在手机上它看起来不像预期的那样

[截图 (887)](https://res.cloudinary.com/practicaldev/image/fetch/s--CIFGGNlH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/eo0opf05s3nj39r0xviu.png)

这就是离子设置的全部内容,我很想浏览应用程序,但不,我会专门为它写一篇文章,在下一篇专门介绍编辑器之后,同时你可以熟悉离子的基础知识

在此之前您可以关闭 ionic 应用程序,我们已完成设置,现在我们开始编写代码。

振作起来,我们现在将更快地行动,并实施更酷的东西,如果你正在阅读这篇文章,谢谢你回来,

即将到来:编辑

问题或想打个招呼,最好的方法是推特:

推特网址

第 3 部分 && 4 即将推出,可能在两三天后

第 3 部分

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐