精华内容
下载资源
问答
  • Please add one of it to your project ! 问题分析: 1、使用PinyinUtil类时需要引入一个第三方的拼音库。例如pinyin4j。 <dependency> <groupId>com.belerweb</groupId> .

     问题描述:

    cn.hutool.extra.pinyin.PinyinException: No pinyin jar found ! Please add one of it to your project !

    问题分析:

    1、使用PinyinUtil类时需要引入一个第三方的拼音库。例如pinyin4j。

            <dependency>
                <groupId>com.belerweb</groupId>
                <artifactId>pinyin4j</artifactId>
                <version>2.5.0</version>
            </dependency>

     

    展开全文
  • Now we will move our task to a standalone project, so we can publish it and share it with others. This project is simply a Groovy project that produces a JAR containing the task class. Here is a ...

    本文禁止w3cschool转载!

    翻译项目请关注Github上的地址:https://github.com/msdx/gradledoc 。

    本文翻译所在分支:https://github.com/msdx/gradledoc/tree/2.0 。
    更好的阅读体验请访问:http://gradledoc.githang.com/2.0/userguide/userguide.html 。
    另外,Android 手机用户可通过我写的一个程序浏览文档,带缓存功能的,目前0.6.1版本兼容 Android 4.0.3以上系统,项目地址如下:
    https://github.com/msdx/gradle-doc-apk

    翻译不易,本文采用 CC BY-NC-SA 4.0 许可协议,转载请务必署名及注明本文在CSDN博客上的出处:

    https://coder.blog.csdn.net/article/details/103430608

    关于我对Gradle的翻译,以Github上的项目及http://gradledoc.githang.com 上的文档为准。如发现翻译有误的地方,将首先在以上两个地方更新。因时间精力问题,博客中发表的译文基本不会同步修改。

    第五十七章. 编写自定义任务类

    Chapter 57. Writing Custom Task Classes

    Gradle 支持两种类型的任务。一种是简单任务,你可以使用一个动作闭包来定义它。我们在《第六章,构建脚本基础》中已经看到过。对于这种类型的任务,动作闭包确定了任务的行为。这种类型的任务便于实现构建脚本中只执行一次的任务。
    Gradle supports two types of task. One such type is the simple task, where you define the task with an action closure. We have seen these in Chapter 6, Build Script Basics. For this type of task, the action closure determines the behaviour of the task. This type of task is good for implementing one-off tasks in your build script.

    另一种是增强任务,它把行为建立在任务中,并且任务提供了一些属性用于配置行为。我们在《第十五章,有关任务的详细信息》中看到过。大多数 Gradle 插件使用增强任务。通过增强任务,你不需要像使用简单任务一样实现任务行为。你只需要简单地声明该任务,并使用其属性来配置它。通过这种方式,增强任务允许让你在许多不同位置,甚至是跨不同构建,来复用某个行为。
    The other type of task is the enhanced task, where the behaviour is built into the task, and the task provides some properties which you can use to configure the behaviour. We have seen these in Chapter 15, More about Tasks. Most Gradle plugins use enhanced tasks. With enhanced tasks, you don't need to implement the task behaviour as you do with simple tasks. You simply declare the task and configure the task using its properties. In this way, enhanced tasks let you reuse a piece of behaviour in many different places, possibly across different builds.

    增强任务的行为和属性由任务的类定义。当你声明一个增强任务时,要指定该任务的类型或类。
    The behaviour and properties of an enhanced task is defined by the task's class. When you declare an enhanced task, you specify the type, or class of the task.

    在 Gradle 中实现自己的自定义任务类并不难。你可以使用你喜欢的任何语言来实现一个自定义任务类,只要它最终会被编译成字节码。在我们的示例中,我们将使用 Groovy 作为实现语言,但你也可以使用 Java 或 Scala。通常来说,使用 Groovy 是一种最简单的选择,因为 Gradle API 就是被设计为方便使用 Groovy 的。
    Implementing your own custom task class in Gradle is easy. You can implement a custom task class in pretty much any language you like, provided it ends up compiled to bytecode. In our examples, we are going to use Groovy as the implementation language, but you could use, for example, Java or Scala. In general, using Groovy is the easiest option, because the Gradle API is designed to work well with Groovy.

    57.1. 封装一个任务类

    57.1. Packaging a task class

    有几个地方可以放任务类的源码。
    There are several places where you can put the source for the task class.

    构建脚本
    Build script

    你可以直接在构建脚本中包含任务类。这样做的好处是,你不需要再另外做什么,这个任务类就会自动编译并包含在构建脚本的类路径中。但是在构建脚本之外,这个任务类是不可见的,因此你无法在定义这个它的构建脚本以外的地方复用这个任务类。
    You can include the task class directly in the build script. This has the benefit that the task class is automatically compiled and included in the classpath of the build script without you having to do anything. However, the task class is not visible outside the build script, and so you cannot reuse the task class outside the build script it is defined in.

    buildSrc 项目
    buildSrc project

    您可以将任务类的源码放在 rootProjectDir/buildSrc/src/main/rootvy 目录中。Gradle 将会编译和测试这个任务类,并使其在构建脚本的类路径中可用。这个任务类对该构建所使用的每个构建脚本都可见。然而,它在构建外部不可见,因此你无法在定义这个任务类的构建之外的其他地方来复用它。使用 buildSrc 项目方法能够保持任务声明——即任务应该做什么,与任务实现——即任务是怎么做的之间相互独立。
    You can put the source for the task class in the rootProjectDir/buildSrc/src/main/groovy directory. Gradle will take care of compiling and testing the task class and making it available on the classpath of the build script. The task class is visible to every build script used by the build. However, it is not visible outside the build, and so you cannot reuse the task class outside the build it is defined in. Using the buildSrc project approach keeps separate the task declaration - that is, what the task should do - from the task implementation - that is, how the task does it.

    有关buildSrc 项目的更详细信息,请参阅《第五十九章,组织构建逻辑》。
    See Chapter 59, Organizing Build Logic for more details about the buildSrc project.

    独立项目
    Standalone project

    你可以为你的任务类创建单独的项目。这个项目会生成和发布一个 JAR,你可以在多个构建中使用并分享出去。通常,这个 JAR 可能包含一些定制插件,或者将多个相关任务类捆绑到一个库中,或两者皆有。
    You can create a separate project for your task class. This project produces and publishes a JAR which you can then use in multiple builds and share with others. Generally, this JAR might include some custom plugins, or bundle several related task classes into a single library. Or some combination of the two.

    在我们的例子中,为了简单,我们将从在构建脚本中定义任务类开始。然后我们看一下创建一个独立的项目的方式。
    In our examples, we will start with the task class in the build script, to keep things simple. Then we will look at creating a standalone project.

    57.2. 编写一个简单的任务类

    57.2. Writing a simple task class

    要实现自定义任务类,请扩展 DefaultTask
    To implement a custom task class, you extend DefaultTask.

    示例 57.1. 定义一个自定义任务 - Example 57.1. Defining a custom task

    build.gradle

    class GreetingTask extends DefaultTask {
    }

    这个任务不执行任何有用的操作,所以我们来添加一些行为。为此,我们向任务添加一个方法,并使用 TaskAction 注解标记它。当任务执行时,Gradle 将调用这个方法。你不必使用方法来定义任务的行为。比如,你可以在任务构造方法中调用 doFirst() 或 doLast() 并传入一个闭包来添加行为。
    This task doesn't do anything useful, so let's add some behaviour. To do so, we add a method to the task and mark it with the TaskAction annotation. Gradle will call the method when the task executes. You don't have to use a method to define the behaviour for the task. You could, for instance, call doFirst() or doLast() with a closure in the task constructor to add behaviour.

    示例 57.2. 一个 hello world 任务 - Example 57.2. A hello world task

    build.gradle

    task hello(type: GreetingTask)
    
    class GreetingTask extends DefaultTask {
        @TaskAction
        def greet() {
            println 'hello from GreetingTask'
        }
    }

    gradle -q hello 的输出结果
    Output of gradle -q hello

    > gradle -q hello
    hello from GreetingTask

    让我们添加一个属性到任务中,这样我们就可以自定义它。任务只是 POGO,当你声明一个任务时,可以在这个任务对象上设置属性或调用方法。这里我们添加了一个 greeting 属性,并在声明 greeting 任务时设置它的值。
    Let's add a property to the task, so we can customize it. Tasks are simply POGOs, and when you declare a task, you can set the properties or call methods on the task object. Here we add a greeting property, and set the value when we declare the greeting task.

    示例 57.3. 一个自定义的 hello world 任务 - Example 57.3. A customizable hello world task

    build.gradle

    // Use the default greeting
    task hello(type: GreetingTask)
    
    // Customize the greeting
    task greeting(type: GreetingTask) {
        greeting = 'greetings from GreetingTask'
    }
    
    class GreetingTask extends DefaultTask {
        String greeting = 'hello from GreetingTask'
    
        @TaskAction
        def greet() {
            println greeting
        }
    }

    gradle -q hello greeting 的输出结果
    Output of gradle -q hello greeting

    > gradle -q hello greeting
    hello from GreetingTask
    greetings from GreetingTask

    57.3. 独立项目

    57.3. A standalone project

    现在我们将任务移到一个独立的项目中,这样我们就可以发布它,并与他人共享。这个项目只是一个 Groovy 项目,其将生成包含任务类的 JAR。以下是该项目的一个简单的构建脚本。它应用了 Groovy 插件,并将 Gradle API 添加为编译时依赖。
    Now we will move our task to a standalone project, so we can publish it and share it with others. This project is simply a Groovy project that produces a JAR containing the task class. Here is a simple build script for the project. It applies the Groovy plugin, and adds the Gradle API as a compile-time dependency.

    示例 57.4. 一个自定义任务的构建 - Example 57.4. A build for a custom task

    build.gradle

    apply plugin: 'groovy'
    
    dependencies {
        compile gradleApi()
        compile localGroovy()
    }

    注意:此例子的代码可以在 Gradle 的二进制文件或源码里的 samples/customPlugin/plugin 中看到。
    Note: The code for this example can be found at samples/customPlugin/plugin which is in both the binary and source distributions of Gradle.

    我们只是按照约定将任务类的源码放在对应的位置。.
    We just follow the convention for where the source for the task class should go.

     

    示例 57.5. 自定义任务 - Example 57.5. A custom task

    src/main/groovy/org/gradle/GreetingTask.groovy

    package org.gradle
    
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    
    class GreetingTask extends DefaultTask {
        String greeting = 'hello from GreetingTask'
    
        @TaskAction
        def greet() {
            println greeting
        }
    }

    57.3.1. 在另一个项目中使用你的任务类

    57.3.1. Using your task class in another project

    要在一个构建脚本中使用一个任务类,你需要将这个类添加到构建脚本的类路径中。要做到这一点,请使用《第 59.5 节,“构建脚本的外部依赖”》中所述的 buildscript { } 块。以下示例展示了当包含任务类的 JAR 已发布到本地存储库时,你可以如何操作。
    To use a task class in a build script, you need to add the class to the build script's classpath. To do this, you use a buildscript { } block, as described in Section 59.5, “External dependencies for the build script”. The following example shows how you might do this when the JAR containing the task class has been published to a local repository:

    示例 57.6. 在另一个项目中使用自定义任务 - Example 57.6. Using a custom task in another project

    build.gradle

    buildscript {
        repositories {
            maven {
                url uri('../repo')
            }
        }
        dependencies {
            classpath group: 'org.gradle', name: 'customPlugin', version: '1.0-SNAPSHOT'
        }
    }
    
    task greeting(type: org.gradle.GreetingTask) {
        greeting = 'howdy!'
    }

    57.3.2. 为你的任务类编写测试

    57.3.2. Writing tests for your task class

    你可以使用 ProjectBuilder 类来创建 Project 实例以在测试任务类时使用。
    You can use the ProjectBuilder class to create Project instances to use when you test your task class.

     

    示例 57.7. 测试自定义任务 - Example 57.7. Testing a custom task

    src/test/groovy/org/gradle/GreetingTaskTest.groovy

    class GreetingTaskTest {
        @Test
        public void canAddTaskToProject() {
            Project project = ProjectBuilder.builder().build()
            def task = project.task('greeting', type: GreetingTask)
            assertTrue(task instanceof GreetingTask)
        }
    }

    57.4. 增量任务

    57.4. Incremental tasks

    增量任务还是一个实验性 功能。
    Incremental tasks are an incubating feature.

    从上面所述的实现的引入(在 Gradle 1.6 发布周期的早期)以来,Gradle 社区内部的讨论中就产生了一些出色的想法,将有关任务实现者的更改信息暴露到下面所述内容中。因此,这个功能的 API 几乎可以肯定将在即将推出的版本中进行更改。但是,请对当前的实现做实现,并与 Gradle 社区分享你的经验。
    Since the introduction of the implementation described above (early in the Gradle 1.6 release cycle), discussions within the Gradle community have produced superior ideas for exposing the information about changes to task implementors to what is described below. As such, the API for this feature will almost certainly change in upcoming releases. However, please do experiment with the current implementation and share your experiences with the Gradle community.

    这个功能孵化过程是 Gradle 功能生命周期的一部分(参见《附录 C,功能的生命周期》),它存在的目的是为了整合早期用户的反馈内容来确保高质量的最终实现。
    The feature incubation process, which is part of the Gradle feature lifecycle (see Appendix C, The Feature Lifecycle), exists for this purpose of ensuring high quality final implementation through incorporation of early user feedback.

    通过 Gradle,实现一个在所有输入和输出都是最新状态时跳过的任务是非常简单的(参见《第 15.9 节,“跳过最新的任务”)。然而,有时候从上次执行以来,只有少数输入文件发生了更改,而你想避免重新处理所有未修改的输入。这对于将输入文件按1:1的基础转换为输出文件的转换任务来说特别有用。
    With Gradle, it's very simple to implement a task that gets skipped when all of it's inputs and outputs are up to date (see Section 15.9, “Skipping tasks that are up-to-date”). However, there are times when only a few input files have changed since the last execution, and you'd like to avoid reprocessing all of the unchanged inputs. This can be particularly useful for a transformer task, that converts input files to output files on a 1:1 basis.

    如果你想优化你的构建,以便只处理已过时的输入,你可以使用增量任务执行此操作。
    If you'd like to optimise your build so that only out-of-date inputs are processed, you can do so with an incremental task.

    57.4.1. 实现增量任务

    57.4.1. Implementing an incremental task

    对于一个要增量处理输入的任务,它必须包含一个增量任务操作。这是一个任务操作方法,它包含了一个 IncrementalTaskInputs 参数,该参数提示 Gradle 这个操作将只处理那些更改的输入。
    For a task to process inputs incrementally, that task must contain an incremental task action. This is a task action method that contains a single IncrementalTaskInputs parameter, which indicates to Gradle that the action will process the changed inputs only.

    这个增量任务操作可以提供一个 IncrementalTaskInputs.outOfDate() 操作来处理所有过时的输入文件,以及一个对自上次执行以来已经被删除的文件执行的 IncrementalTaskInputs.removed() 操作。
    The incremental task action may supply an IncrementalTaskInputs.outOfDate() action for processing any input file that is out-of-date, and a IncrementalTaskInputs.removed() action that executes for any input file that has been removed since the previous execution.

    示例 57.8. 定义增量任务操作 - Example 57.8. Defining an incremental task action

    build.gradle

    class IncrementalReverseTask extends DefaultTask {
        @InputDirectory
        def File inputDir
    
        @OutputDirectory
        def File outputDir
    
        @Input
        def inputProperty
    
        @TaskAction
        void execute(IncrementalTaskInputs inputs) {
            println inputs.incremental ? "CHANGED inputs considered out of date" : "ALL inputs considered out of date"
            inputs.outOfDate { change ->
                println "out of date: ${change.file.name}"
                def targetFile = new File(outputDir, change.file.name)
                targetFile.text = change.file.text.reverse()
            }
    
            inputs.removed { change ->
                println "removed: ${change.file.name}"
                def targetFile = new File(outputDir, change.file.name)
                targetFile.delete()
            }
        }
    }

    注︰ 此示例的代码可以在 Gradle 的二进制及源码分发包的 samples/userguide/tasks/incrementalTask 中找到。
    Note: The code for this example can be found at samples/userguide/tasks/incrementalTask which is in both the binary and source distributions of Gradle.

    对于像这样的简单转换任务,任务操作只需要对任何过时的输入生成输出文件,并删除任何已移除的输入的输出文件。
    For a simple transformer task like this, the task action simply needs to generate output files for any out-of-date inputs, and delete output files for any removed inputs.

    一个任务可能只包含一个增量任务操作。
    A task may only contain a single incremental task action.

    57.4.2. 哪些输入是被视为过时的?

    57.4.2. Which inputs are considered out of date?

    当 Gradle 有了前一次任务执行的历史,并且从那一次执行之后任务执行的上下文中只有输入有修改,那么 Gradle 就能够确定这个任务需要重新处理哪些文件。在这种情况下,将对已添加的已修改的的输入文件执行 IncrementalTaskInputs.outOfDate() 操作,并且对任何已移除的输入文件执行 IncrementalTaskInputs.removal() 操作。
    When Gradle has history of a previous task execution, and the only changes to the task execution context since that execution are to input files, then Gradle is able to determine which input files need to be reprocessed by the task. In this case, the IncrementalTaskInputs.outOfDate() action will be executed for any input file that was added or modified, and the IncrementalTaskInputs.removed() action will be executed for any removed input file.

    然而,有许多情况下,Gradle 还是无法确定需要重新处理哪些输入文件。包括比如:
    However, there are many cases where Gradle is unable to determine which input files need to be reprocessed. Examples include:

    • 没有自上次执行以来的有效历史记录。
      There is no history available from a previous execution.
    • 你正在使用不同版本的 Gradle 构建。Gradle 目前不使用来自不同版本的任务历史记录。
      You are building with a different version of Gradle. Currently, Gradle does not use task history from a different version.
    • 添加到任务的 upToDateWhen 条件返回 false
      An upToDateWhen criteria added to the task returns false.
    • 自上次执行以来,输入属性已有修改。
      An input property has changed since the previous execution.
    • 自上次执行以来,一有个或多个的输出文件已更改。
      One or more output files have changed since the previous execution.

    在以上所述的任一情况下,Gradle 将把所有输入文件视为 outOfDate,然后会对每个输入文件执行 IncrementalTaskInputs.outOfDate() 操作,并且将不再执行 IncrementalTaskInputs.removremoval() 操作。
    In any of these cases, Gradle will consider all of the input files to be outOfDate. The IncrementalTaskInputs.outOfDate() action will be executed for every input file, and the IncrementalTaskInputs.removed() action will not be executed at all.

    你可以使用 IncrementalTaskInputs.isIncremental() 来检查 Gradle 是否能够确定输入文件的增量更改。
    You can check if Gradle was able to determine the incremental changes to input files with IncrementalTaskInputs.isIncremental().

    57.4.3. 增量任务的操作

    57.4.3. An incremental task in action

    考虑到上面的增量任务实现,我们可以通过示例探讨各种更改场景。请注意,各种变化的任务(“updateInputs”,“removeInput”等)仅用于演示:它们通常不会在构建脚本中。
    Given the incremental task implementation above, we can explore the various change scenarios by example. Note that the various mutation tasks ('updateInputs', 'removeInput', etc) are only present for demonstration purposes: these would not normally be part of your build script.

    首先,考虑到 IncrementalReverseTask 第一次针对一组输入执行。在这种情况下,所有输入都将认为是“过时的”:
    First, consider the IncrementalReverseTask executed against a set of inputs for the first time. In this case, all inputs will be considered "out of date":

    示例 57.9. 首次运行增量任务 - Example 57.9. Running the incremental task for the first time

    build.gradle

    task incrementalReverse(type: IncrementalReverseTask) {
        inputDir = file('inputs')
        outputDir = file("$buildDir/outputs")
        inputProperty = project.properties['taskInputProperty'] ?: "original"
    }

    构建布局
    Build layout

    incrementalTask/
      build.gradle
      inputs/
        1.txt
        2.txt
        3.txt

    gradle -q incrementalReverse 的输出结果
    Output of gradle -q incrementalReverse

    > gradle -q incrementalReverse
    ALL inputs considered out of date
    out of date: 1.txt
    out of date: 2.txt
    out of date: 3.txt

    当然,当再次执行任务时没有任何修改,那么任务本身是最新的,并且不会向这个任务操作报告任何文件:
    Naturally when the task is executed again with no changes, then task itself is up to date and no files are reported to the task action:

    示例 57.10. 在输入不变时运行增量任务 - Example 57.10. Running the incremental task with unchanged inputs

    gradle -q incrementalReverse 的输出结果
    Output of gradle -q incrementalReverse

    > gradle -q incrementalReverse

    当以某种方式修改了输入文件,或者新增输入文件时,重新执行这个任务会使这些文件被上报到 IncrementalTaskInputs.outOfDate()
    When an input file is modified in some way or a new input file is added, then re-executing the task results in those files being reported to IncrementalTaskInputs.outOfDate():

    示例 57.11. 有输入文件更新时运行增量任务 - Example 57.11. Running the incremental task with updated input files

    build.gradle

    task updateInputs() << {
        file('inputs/1.txt').text = "Changed content for existing file 1."
        file('inputs/4.txt').text = "Content for new file 4."
    }

    gradle -q updateInputs incrementalReverse 的输出结果
    Output of gradle -q updateInputs incrementalReverse

    > gradle -q updateInputs incrementalReverse
    CHANGED inputs considered out of date
    out of date: 1.txt
    out of date: 4.txt

    当删除一个现有的输入文件后,重新执行这个任务会使该文件被报告给 IncrementalTaskInputs.removal()
    When an existing input file is removed, then re-executing the task results that file being reported to IncrementalTaskInputs.removed():

    示例 57.12. 当输入文件被删除时运行增量任务 - Example 57.12. Running the incremental task with an input file removed

    build.gradle

    task removeInput() << {
        file('inputs/3.txt').delete()
    }

    gradle -q removeInput incrementalReverse 的输出结果
    Output of gradle -q removeInput incrementalReverse

    > gradle -q removeInput incrementalReverse
    CHANGED inputs considered out of date
    removed: 3.txt

    当删除(或修改)输出文件时,Gradle 无法确定哪些输入文件是已过时的。在这种情况下,所有的 输入文件都会被报告给 IncrementalTaskInputs.outOfDate() 操作,并且不会向 IncrementalTaskInputs.removal() 操作报告任何输入文件:
    When an output file is deleted (or modified), then Gradle is unable to determine which input files are out of date. In this case, all input files are reported to the IncrementalTaskInputs.outOfDate() action, and no input files are reported to the IncrementalTaskInputs.removed() action:

    示例 57.13. 当输出文件被删除时运行增量任务 - Example 57.13. Running the incremental task with an output file removed

    build.gradle

    task removeOutput() << {
        file("$buildDir/outputs/1.txt").delete()
    }

    gradle -q removeOutput incrementalReverse 的输出结果
    Output of gradle -q removeOutput incrementalReverse

    > gradle -q removeOutput incrementalReverse
    ALL inputs considered out of date
    out of date: 1.txt
    out of date: 2.txt
    out of date: 3.txt

    当修改任务的输入属性时,Gradle 无法确定该属性对任务的输出会有怎样的影响,因此将假定所有输入文件都已过时。所以与更改输出文件的示例类似,所有的输入文件将报告给 IncrementalTaskInputs.outOfDate() 操作,并且不会向 IncrementalTaskInputs.removal() 操作报告任何输入文件:
    When a task input property modified, Gradle is not able to determine how this property impacted the task outputs, so all input files are assumed to be out of date. So similar to the changed output file example, all input files are reported to the IncrementalTaskInputs.outOfDate() action, and no input files are reported to the IncrementalTaskInputs.removed() action:

    示例 57.14. 当输入属性被更改时运行增量任务 - Example 57.14. Running the incremental task with an input property changed

    gradle -q -PtaskInputProperty=changed incrementalReverse 的输出结果
    Output of gradle -q -PtaskInputProperty=changed incrementalReverse

    > gradle -q -PtaskInputProperty=changed incrementalReverse
    ALL inputs considered out of date
    out of date: 1.txt
    out of date: 2.txt
    out of date: 3.txt
    展开全文
  • 本地代码部署到服务器 方法by Andrea Passwater 通过安德里亚·帕斯沃特(Andrea Passwater) 我刚刚部署了无服务器应用程序-我无法编写代码。... Here’s how I did it.) Hey there developer friends...

    本地代码部署到服务器 方法

    by Andrea Passwater

    通过安德里亚·帕斯沃特(Andrea Passwater)

    我刚刚部署了无服务器应用程序-我无法编写代码。 这是我的方法。 (I just deployed a serverless app — and I can’t code. Here’s how I did it.)

    Hey there developer friends! I somehow just managed to deploy a real, working application. But heads up — I am not one of you.

    嘿,有开发者朋友! 我以某种方式设法部署了一个真实的,可运行的应用程序。 但是请注意-我不是你们中的一员。

    I am a writer who honest-to-god composes tweets and blog posts for a living.

    我是一位诚实的人,他撰写推文和博客文章为生。

    My command line experience is limited to a Codecademy course I took back before the iPhone 5 was released. I know how to type ls to see the contents of a folder without visual styling. Tl;dr I’m a hacker badass.

    我的命令行经验仅限于在发布iPhone 5之前收回的Codecademy课程。 我知道如何在没有视觉样式的情况下键入ls来查看文件夹的内容。 Tl; dr,我是骇客。

    All the same, friends, I wrote an app. And it’s hosted. And you can go visit it yourself, from your own personal computer.

    朋友们,我都写了一个应用程序。 它是托管的。 您可以从自己的个人计算机上亲自访问它。

    Because, as I have discovered, AWS Lambda and the Serverless Framework make it really not that hard to deploy an application.

    因为,正如我所发现的那样, AWS Lambda无服务器框架使部署应用程序确实不那么困难。

    Here’s how you, too, can write and deploy a serverless app with basically zero coding experience.

    这也是您可以编写和部署基本无编码经验的无服务器应用程序的方法。

    好的,但是喜欢……你为什么这样做? (Ok but like…why did you do this?)

    So glad you asked! Because, look — automation is power.

    很高兴你问! 因为,看—自动化就是力量。

    Just like you, I have things — too many things — to do, and not enough resources to get them done.

    就像您一样,我还有很多事情要做,没有足够的资源来完成。

    I want to write slack bots that remind people when their blog drafts are due. I want to create drip campaigns based on user behavior without blowing my budget on fifteen marketing tools. I want to automerge blog posts according to a pre-set schedule.

    我想写一些松弛的机器人来提醒人们博客的到期日。 我想根据用户的行为创建点滴广告系列,而又不花我的预算购买十五种营销工具。 我想根据预设的时间表自动合并博客文章。

    Custom apps give me that. I could create a team of robot minions.

    自定义应用程序给了我。 我可以创建一个机器人奴才团队

    制作应用 (Making the app)

    This whole project took me maybe an hour.

    整个项目大概花了我一个小时。

    Here is what we’re going to do:

    这是我们要做的:

    • Install the Serverless Framework

      安装无服务器框架
    • Create an AWS account

      创建一个AWS账户
    • Set up AWS permissions (IAM roles) for my serverless user

      为我的无服务器用户设置AWS权限(IAM角色)
    • Find some freely-available code on npm that does close enough to what I want

      在npm上找到一些与我想要的足够接近的免费可用代码
    • Steal it

      偷它
    • Tweak it

      调整它
    • Deploy it up to Lambda using the Serverless Framework

      使用无服务器框架将其部署到Lambda

    The cool thing about Lambda is, I don’t have to manage it or provision it or scale it or anything. I throw my code up there, and it runs when it needs to. I can deploy this and basically forget about it.

    关于Lambda的最酷的事情是,我不必对其进行管理,配置或扩展或进行任何扩展。 我将代码放在此处,并在需要时运行。 我可以部署它,而基本上不用管它。

    The cool thing about the Serverless Framework is, AWS is hard to figure out. The Framework takes care of all the details behind the scenes, and frankly makes it pretty impossible to mess up.

    关于无服务器框架的很酷的事情是,AWS很难弄清楚。 该框架负责处理幕后的所有细节,坦率地说,几乎不可能搞砸。

    Case in point: me. I didn’t mess it up.

    例子:我。 我没有搞砸。

    This thing is bullet proof.

    这东西是防弹的。

    安装无服务器框架 (Install the Serverless Framework)

    If you don’t already have Homebrew (I didn’t), you’ll need to install that first. Homebrew makes it easy to install all kinds of developer stuff on your machine.

    如果您还没有Homebrew(我没有),则需要先安装它。 Homebrew使您可以轻松地在计算机上安装各种开发人员资料。

    Open your terminal, and paste the snippet from the Homebrew homepage:

    打开终端,然后从Homebrew主页粘贴该代码段:

    /usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

    Wait an eternity (aka 5 minutes) for it to finish, and then install Node; the Framework requires this to run:

    等待永恒(也就是5分钟)以完成它,然后安装Node。 该框架要求运行以下命令:

    brew install node

    brew install node

    Then paste the snippet from the Serverless.com homepage:

    然后从Serverless.com主页粘贴该代码段:

    npm install serverless -g

    npm install serverless -g

    Congratulations! You’ve made it this far using only copy/paste.

    恭喜你! 到目前为止,您仅使用复制/粘贴即可实现。

    创建一个AWS账户 (Create an AWS account)

    Go to aws.amazon.com and click one of the fifteen helpful buttons that let you create a new account. I chose the one in the top right corner:

    转到aws.amazon.com并单击十五个有用的按钮之一,这些按钮可让您创建一个新帐户。 我选择了右上角的一个:

    They will make you answer a lot of questions. Sorry about that. This will all be over soon.

    他们会让您回答很多问题。 对于那个很抱歉。 这将很快结束。

    When they ask for you credit card, you’ll have to give them one. They don’t charge you for anything up front, but they keep it on file just in case they ever have to.

    当他们要求您提供信用卡时,您必须给他们一张。 他们不会预先向您收取任何费用,但会保留在案以防万一。

    Ok I lied about the “over soon” part. Now they’ll want to verify your identity by having a robot call you on the phone (lol). Just appease them, we’re seriously almost done this time.

    好吧,我对“很快”部分撒了谎。 现在,他们希望通过让机器人通过电话给您打电话来验证您的身份(笑)。 只是安抚他们,这次我们几乎认真完成了。

    When your phone rings, enter the 4-digit PIN on your screen. I looked at the timer on my phone; it was a 20 second call.

    电话响起时,在屏幕上输入4位PIN码。 我看着手机上的计时器。 这是一个20秒的通话。

    Finally, choose your support plan! Which is to say, do not choose a support plan. Unless you are a real company, I guess, in which case spend those investor dollars however you want.

    最后,选择您的支持计划! 也就是说,不要选择支持计划。 除非您是一家真正的公司,否则我认为,在这种情况下,可以根据需要花费那些投资者的资金。

    Here I am not being a real company and choosing the “Basic” option because it’s free:

    在这里,我不是一家真正的公司,而是选择“基本”选项,因为它是免费的:

    Now go ahead and click “Launch the console” and log in again.

    现在,继续并单击“启动控制台”,然后再次登录。

    All said and done, this stage takes 5–10 minutes, depending on whether or not you (humble brag) have your credit card number memorized because you are a prolific online shopper.

    总而言之,这个阶段需要5到10分钟,具体取决于您(谦虚的吹牛)是否记住了您的信用卡号码,因为您是一位多产的在线购物者。

    配置IAM用户 (Configure an IAM user)

    The Serverless Framework needs this in order to do all the complicated Lambda setup stuff on your behalf.

    无服务器框架需要这样做,以便代表您执行所有复杂的Lambda设置工作。

    Sounds like a great trade to me! Let’s do it.

    听起来对我来说是一笔巨大的交易! 我们开始做吧。

    It’s worth noting that I used this incredibly helpful walkthrough to guide me throughout the entire setup process. You might like it too.

    值得注意的是,我使用了非常有用的演练来指导我完成整个设置过程。 您可能也喜欢。

    Now that you’ve launched your AWS console, type “iam” into that handy search box:

    现在,您已经启动了AWS控制台,在方便的搜索框中输入“ iam”:

    Why yes, we would like to manage “User Access” and “Encryption Keys.” Click dat.

    为什么会这样,我们要管理“用户访问权限”和“加密密钥”。 单击数据。

    Once you’re inside IAM, go to “Users” in the lefthand menu:

    进入IAM后,转到左侧菜单中的“用户”:

    And then “Add user” up top:

    然后在顶部添加“添加用户”:

    Now we need to configure this new user.

    现在我们需要配置这个新用户。

    You can either watch this 75 second video and do exactly what they do, or if you’re a nerd for abstractions, you can scroll through these screenshots of me doing exactly what they do and copy those instead.

    您可以观看这75秒的视频并完全按照他们的方式做, 或者,如果您是抽象的书呆子,则可以滚动浏览这些我的屏幕截图 完全按照他们所做的去做,然后复制它们。

    Create any user name you want and check the box beside “Programmatic access”:

    创建所需的任何用户名,然后选中“程序访问”旁边的框:

    Click “Attach existing policies directly” and then check the box beside “AdministratorAccess”:

    单击“直接附加现有策略”,然后选中“ AdministratorAccess”旁边的框:

    It’s worth noting that, according to this Serverless.com blog post on IAM, taking the “AdministratorAccess” route is the “Fast but risky YOLO method.” I’m going to do it anyway because I’m not a developer and therefore do not understand the havoc I may wreak.

    值得注意的是,根据Serverless.com上有关IAM的博客文章 ,采用“ AdministratorAccess”路线是“快速但有风险的YOLO方法”。 无论如何,我都会这样做,因为我不是开发人员,因此无法理解我可能造成的破坏。

    Please don’t hack me.

    请不要砍我。

    If you want to be extra diligent about it, follow their guidelines on the slow but safe approach.

    如果您想更加努力,请遵循他们缓慢但安全的方法指南

    Go ahead and click “Next” and then “Create user.”

    继续,单击“下一步”,然后单击“创建用户”。

    You will land on a screen like this. Don’t close this window yet, we’re about to need it:

    您将在这样的屏幕上着陆。 暂时不要关闭此窗口,我们将需要它

    安装AWS CLI (Install AWS CLI)

    This will let us (1) do stuff with AWS without poking through their intimidating interface with too many icons; (2) mindlessly copy/paste stuff into the terminal that experts on the internet say will do what we want.

    这样一来,我们(1)可以使用AWS进行操作,而不必通过带有太多图标的令人恐惧的界面来戳戳; (2)漫不经心地将东西复制/粘贴到终端上,互联网专家说这将完成我们想要的事情。

    To start off this copy/paste party, put brew install awscli in your terminal to make Homebrew install the AWS CLI for you.

    要开始此复制/粘贴方, brew install awscli在终端中放入brew install awscli以使Homebrew为您安装AWS CLI。

    After that’s done, type aws configure.

    完成后,输入aws configure

    Now you’ll need that IAM user window, which you definitely haven’t closed! Copy the “Access key ID” and “Secret access key” from that window and paste them into the terminal as prompted.

    现在,您将需要您尚未关闭的IAM用户窗口! 从该窗口复制“访问密钥ID”和“秘密访问密钥”,然后按照提示将其粘贴到终端中。

    I’m leaving the other stuff blank because (as we’ve already covered) YOLO:

    我将其他内容留空,因为(如我们所介绍的)YOLO:

    AWS Access Key ID [None]: PLZ-PUT-UR-ACCESS-KEY-HEREAWS Secret Access Key [None]: PLZ-PUT-UR-SECRET-ACCESS-KEY-HEREDefault region name [None]: Default output format [None]:

    冲浪NPM以获取预写的代码 (Surfing NPM for pre-written code)

    Since I’m a hipster with an ironic sense of humor, I decided to make my first serverless app a Serverless Ipsum generator.

    因为我是一个具有讽刺意味的幽默感的时髦人士,所以我决定将我的第一个无服务器应用程序变成无服务器Ipsum生成器。

    It’s like Lorem Ipsum, but with randomized “serverless movement” buzzwords. Get it. It’s a serverless Serverless app. ?

    就像Lorem Ipsum,但带有随机的“无服务器移动”流行语。 得到它。 这是一个无服务器的无服务器应用程序。 ?

    I went over to npmjs.com, which is a magical website where people with hearts much bigger than mine post their NodeJS code so other people can use it. …for free. I still can’t believe this actually exists.

    我去了npmjs.com ,这是一个神奇的网站,在那里,比我大得多的人发布了他们的NodeJS代码,以便其他人可以使用它。 …免费。 我仍然不敢相信这确实存在。

    I typed “lorem ipsum” into the search box and evaluated my options.

    我在搜索框中输入了“ lorem ipsum”并评估了我的选择。

    I clicked the one that was simply called “lorem-ipsum,” because as a professional writer I have an affinity for word choice minimalism. (Thanks knicklabs!)

    我单击了一个简单的名称,即“ lorem-ipsum”,因为作为专业作家,我非常喜欢单词选择的简约性。 (感谢knicklabs !)

    Let’s snag it.

    让我们抓住它。

    But first we need to create a place to put it. A new serverless project, if you will.

    但是首先我们需要创建一个放置它的地方。 如果可以的话,这是一个新的无服务器项目。

    使用无服务器框架创建新项目 (Using the Serverless Framework to create a new project)

    Remember at the top of this post, when we did npm install serverless -g? That installed the Serverless Framework, which we’re now going to use!

    还记得这篇文章的npm install serverless -g ,我们何时npm install serverless -g吗? 安装了无服务器框架,我们现在将使用它!

    First, let’s make a folder to keep all of our numerous future serverless projects in. I’m going to call my folder “Code” because it rhymes with Node and I’m a poet.

    首先,让我们创建一个文件夹,以保存所有将来的众多无服务器项目。我将我的文件夹称为“代码”,因为它与Node押韵并且我是诗人。

    Here’s where my badass Codecademy hacker skills become relevant. In your terminal, type ls to see all your folder names. (I don’t actually think there’s a reason you need do this, but there’s no reason not to do it either, so might as well get warmed up.)

    这是我的坏蛋Codecademy黑客技能变得与之相关的地方。 在终端中,键入ls以查看所有文件夹名称。 (实际上,我不认为您要这样做,但也没有理由不这样做,所以不妨热身。)

    You should be looking at a list of all the folders on your desktop right now, in a clean monospaced font. Magical.

    您应该现在以干净的等宽字体查看桌面上所有文件夹的列表。 神奇。

    Now type mkdir Code — aka, “make a directory (folder) called ‘Code.’’’ Then cd Code to navigate there.

    现在,键入mkdir Code又名“建立一个名为'Code'的目录(文件夹)。“,然后cd Code导航到那里。

    Inside this brand new “Code” folder, I am going to create my very first serverless app ever. Watch me go:

    在这个全新的“ Code”文件夹中,我将创建我的第一个无服务器应用程序。 看着我走:

    serverless create --template aws-nodejs --path serverless-ipsum

    serverless create --template aws-nodejs --path serverless-ipsum

    The--template aws-nodejs part tells Serverless that we are using AWS and NodeJS. They’ll use that info to do magical setup/configuration tricks on our behalf.

    --template aws-nodejs部分告诉Serverless我们正在使用AWS和NodeJS。 他们将使用该信息代表我们执行神奇的设置/配置技巧。

    The --path serverless-ipsum part tells Serverless that our new project is called “serverless-ipsum.” So you should replace serverless-ipsum with whatever you want your project name to be.

    --path serverless-ipsum部分告诉Serverless我们的新项目称为“ serverless-ipsum”。 因此,您应使用希望项目名称为任何名称的值来替换serverless-ipsum。

    When you press `enter`, Serverless will create a new folder called “serverless-ipsum.” All of your application stuff will live there.

    当您按Enter键时,Serverless将创建一个名为“ serverless-ipsum”的新文件夹。 您所有的应用程序资料都将存在于此。

    We just made the beginnings of an app. Now let’s give it some code to run.

    我们只是一个应用程序的开始。 现在让我们给它一些代码来运行。

    从NPM安装该lorem ipsum软件包 (Installing that lorem ipsum package from NPM)

    Navigate to that serverless-ipsum directory we just created by typing cd serverless-ipsum in your terminal.

    通过在终端中输入cd serverless-ipsum导航到我们刚刚创建的该serverless-ipsum目录。

    Then type npm install lorem-ipsum to install the lorem ipsum package from NPM.

    然后键入npm install lorem-ipsum以从NPM安装lorem ipsum软件包。

    Now our app folder has code in it! It’s basically an app already! Sort of.

    现在,我们的应用程序文件夹中包含代码! 基本上已经是一个应用程序了! 有点。

    调整该NPM代码 (Tweaking that NPM code)

    We do have to make some tweaks to that code, so go on and open up your favorite code editor. I used Atom because I do not have a favorite code editor but I do have a favorite unit of matter.

    我们确实需要对该代码进行一些调整,因此继续打开您喜欢的代码编辑器。 我之所以使用Atom,是因为我没有喜欢的代码编辑器,但确实有喜欢的单元。

    Anyway this is what your handler.js file should look like right now:

    无论如何,这是您现在的handler.js文件应为:

    Open it up and replace the stuff up top with this:

    打开它,并用以下内容替换上面的内容:

    'use strict';
    const ipsum = require("lorem-ipsum")
    module.exports.hello = (event, context, callback) => {  const response = {    statusCode: 200,    body: ipsum(),  };
    callback(null, response);};

    Such that your handler.js file now looks like this:

    这样您的handler.js文件现在如下所示:

    What we did here was tell our little function handler to run that lorem-ipsum package we downloaded and print the output for us.

    我们在这里所做的是告诉我们的小函数处理程序运行我们下载的lorem-ipsum软件包并为我们打印输出。

    We first required the package up top with const ipsum = require("lorem-ipsum"), and then told the body to print out that generated ipsum with body: ipsum(). Everything else stayed the same.

    我们首先需要使用const ipsum = require("lorem-ipsum")打包程序,然后告诉body使用body: ipsum()打印出生成的ipsum。 其他一切保持不变。

    在终端本地测试 (Testing it out locally in the terminal)

    Guys. We have created a serverless project in the Framework. We have downloaded some pre-written code that generates lorem ipsum.

    伙计们 我们在框架中创建了一个无服务器项目。 我们已经下载了一些生成lorem ipsum的预编写代码。

    We can tell Serverless to run this. Right now. In our terminals.

    我们可以告诉Serverless运行它。 马上。 在我们的航站楼。

    Moment of truth. Type: serverless invoke local --function hello

    关键时刻。 类型: serverless invoke local --function hello

    Wait for it…

    等等...

    {“statusCode”: 200,“body”: “Amet cillum est dolor eiusmod elit eiusmod nulla eu do.”}

    Oh my god.

    哦,我的上帝。

    调整预建的lorem ipsum生成器 (Tweaking that pre-built lorem ipsum generator)

    Latin is great, but serverless gibberish is better. We are, after all, making a Serverless Ipsum generator.

    拉丁文很棒,但无服务器乱码更好。 毕竟,我们正在制造无服务器 Ipsum生成器。

    Go back to Atom, where your “serverless-ipsum” project is probably still open. It contains several files, and we’re going to make it contain one more.

    回到Atom,您的“ serverless-ipsum”项目可能仍处于打开状态。 它包含几个文件,我们将使其再包含一个文件。

    Hit cmd/ctrl + n to create a new file, and name it dictionary.js. Create an array and fill it with industry buzzwords like “Lambda” and “serverdeath”:

    cmd/ctrl + n创建一个新文件,并将其命名为dictionary.js 。 创建一个数组 ,并用“ Lambda”和“ serverdeath”等行业流行词填充它:

    For those of you who are exclusively on the copy/paste train, here you go:

    对于那些专门在复制/粘贴火车上的人,请前往:

    module.exports = ['auto-scaling','zero-maintenance','pay-per-execution','serverdeath','function','event','handler','cloud','NoOps','Lambda','microservices','monitoring',]

    Then save the file with cmd/ctrl + s.

    然后使用cmd/ctrl + s保存文件。

    We also have to go back into our handler.js file, and tell it to use the dictionary file we just made.

    我们还必须返回到handler.js文件,并告诉它使用我们刚刚制作的字典文件。

    So click in to handler.js, and stick this up top:

    因此,单击进入handler.js ,并将其粘贴在顶部:

    const dictionary = require("./dictionary")

    Now go into the body of your function again, and tell it to pull all its words from the dictionary you just made, i.e.:

    现在再次进入函数的body ,并告诉它从您刚创建的字典中拉出所有单词,即:

    module.exports.hello = (event, context, callback) => {  const response = {    statusCode: 200,    body: ipsum({      words: dictionary,    }),  };

    Your handler.js file should now look like this:

    您的handler.js文件现在应如下所示:

    Let’s try it out locally again, and see if the output is Serverless Ipsum instead of Lorem Ipsum. Go back to the terminal and re-type that --function command from before:

    让我们在本地再次尝试,看看输出是否是无服务器Ipsum而不是Lorem Ipsum。 返回终端并从之前重新输入--function命令:

    serverless invoke local --function hello

    serverless invoke local --function hello

    Wait for it…

    等等...

    {“statusCode”: 200,“body”: “Multi-cloud openWhisk google cloud functions lambda source services signature function monitoring zero-maintenance monitoring multi-cloud azure.”}

    This is literally the most beautiful thing I’ve ever seen.

    从字面上看,这是我见过的最美丽的东西。

    让我们把这个东西放到互联网上 (Let’s put this thing on the internet)

    Yeah yeah, ok, so it works on your personal laptop. But real devs got their stuff available on Chrome.

    是的,好的,因此可以在您的个人笔记本电脑上使用。 但是真正的开发人员可以在Chrome上使用他们的东西。

    That takes one more tiny step.

    这又迈出了一步。

    调整serverless.yml (Tweaking that serverless.yml)

    The serverless YAML (serverless.yml) is the configuration file Serverless uses to manage the functions (aka, bits of code) you deploy to Lambda. We need to tell it to make a little website for us.

    无服务器YAML( serverless.yml )是Serverless用来管理部署到Lambda的功能(又名代码位)的配置文件。 我们需要告诉它为我们制作一个小网站。

    This is what our serverless-ipsum YAML file currently looks like (all comments collapsed for concision):

    这是我们的serverless-ipsum YAML文件当前的样子(为了简明起见,所有注释都折叠了):

    We’re going to change a couple things in the functions portion.

    我们将在functions部分中进行一些更改。

    That “hello” stuff is by default. We’re going to replace it with “ipsum,” because I rather reasonably decided that I wanted to run Serverless Ipsum with the command ipsum. (Note: This means that from now on, you’ll run --function ipsum instead of --function hello.)

    默认情况下,这些“ hello”东西是。 我们将其替换为“ ipsum”,因为我相当合理地决定我想使用ipsum命令运行无服务器Ipsum。 ( 注意:这意味着从现在开始,您将运行--function ipsum而不是--function hello 。)

    Then we’re going to tell the function handler that, when we run ipsum, what we actually want it to do is post Serverless Ipsum to a public URL via HTTP request.

    然后,我们将告诉函数处理程序,当我们运行ipsum ,我们真正希望它执行的操作是通过HTTP请求将Serverless Ipsum发布到公共URL。

    Blah blah blah just go to line 57 in your editor and replace all that stuff with this:

    等等等等,只需转到编辑器中的第57行,并使用以下内容替换所有内容:

    functions:  ipsum:    handler: handler.ipsum    events:      - http:          method: get          path: /

    And now your serverless.yml should look like this!

    现在您的serverless.yml应该看起来像这样!

    Go back to your handler.js file and change module.exports.hello to say module.exports.ipsum. This is because we changed our function name from “hello” to “ipsum.”

    回到你的handler.js文件并更改module.exports.hellomodule.exports.ipsum 。 这是因为我们将函数名称从“ hello”更改为“ ipsum”。

    So now your handler.js file looks like this:

    因此,现在您的handler.js文件如下所示:

    部署部署部署! (Deploy deploy deploy!)

    It’s time. We are ready to launch this thing for real.

    是时候了。 我们已经准备好将其发布为真实。

    Get yourself in that terminal.

    让自己进入那个终端。

    Type serverless deploy.

    键入serverless deploy

    Hold your breath.

    屏住呼吸。

    And —

    还有-

    (!!!)

    (!!!)

    BOOM.

    繁荣。

    Take that url beside `GET`: https://791qej6263.execute-api.us-east-1.amazonaws.com/dev/

    以`GET`旁边的网址: https ://791qej6263.execute-api.us-east-1.amazonaws.com/dev/

    Copy/paste it into your browser, and behold:

    将其复制/粘贴到浏览器中,请注意:

    Our app. Is alive. On the internet!

    我们的应用程序。 活着。 在网上!

    我仍然要做的事情 (Things I still have to do)

    Ok so, let’s be real. The UI is not pretty. I plan to actually get a domain for this and give it some sort of simple frontend so it looks at least plausibly professional.

    好吧,让我们成为现实。 用户界面不漂亮。 我计划为此实际获得一个域,并为其提供一些简单的前端,以便看起来至少看起来很专业。

    Another day, friends. Another day.

    有一天,朋友们。 另一天。

    总共 (In sum)

    I am a writer. I can’t code. But I made a serverless application and I put it on the internet for all to see.

    我是一个作家。 我不会编码。 但是我制作了一个无服务器应用程序,然后将其放置在互联网上以供所有人查看。

    Serverless: literally anyone can do it.

    无服务器: 实际上任何人都可以做到

    AWS: not that scary after all.

    卓:毕竟不那么恐怖

    Hope you all use this to make something cool.

    希望大家都用它来制作一些很棒的东西。

    翻译自: https://www.freecodecamp.org/news/i-just-deployed-a-serverless-app-and-i-cant-code-here-s-how-i-did-it-94983d7b43bd/

    本地代码部署到服务器 方法

    展开全文
  • There are a lot of different options available when it comes to source maps, be sure to check them out so you can configure them to your needs.  源地图有很多不同的选项可供选择,一定要检查一下,以便...

    Development

    This guide extends on code examples found in the Output Management guide.

           本指南的例子是基于上一个指南的。(≖ᴗ≖)✧

    If you've been following the guides, you should have a solid understanding of some of the webpack basics. Before we continue, let's look into setting up a development environment to make our lives a little easier.

           如果你一直跟着我们的指南敲敲敲(=´ω`=),你现在应该对于webpack的基础有一个很扎实的理解了。在我们继续之前,让我们深入研究一下搭建开发环境,这可以使我们的生活不那么艰辛!(ノ´▽`)ノ♪

    The tools in this guide are only meant for development, please avoid using them in production!!

           下面提到的工具,只能在开发时使用,请避免在产品中还用它们!

    Using source maps (使用源地图)

    When webpack bundles your source code, it can become difficult to track down errors and warnings to their original location. For example, if you bundle three source files (a.js, b.js, and c.js) into one bundle (bundle.js) and one of the source files contains an error, the stack trace will simply point to bundle.js. This isn't always helpful as you probably want to know exactly which source file the error came from.

           当webpack为你的源码打包时,它很难追踪出errorswarnings的源头。举个例子,如果你正在给三个源文件打包(a.js, b.js, 和 c.js),将它们打包到bundle.js,并且,其中一个文件中包含error,堆栈跟踪会将error简单指向bundle.js.这并不总是有用,因为您可能想知道错误来自哪个源文件(T▽T)

    In order to make it easier to track down errors and warnings, JavaScript offers source maps, which maps your compiled code back to your original source code. If an error originates from b.js, the source map will tell you exactly that.

           为了更容易追踪错误和警告,JavaScript提供了源代码映射,将您编译的代码映射回原始源代码。如果一个错误源于b.js,源地图会告诉你这个事 。(o゚▽゚)o

    There are a lot of different options available when it comes to source maps, be sure to check them out so you can configure them to your needs.

           源地图有很多不同的选项可供选择,一定要检查一下,以便将它们配置为您的需要。

    For this guide, let's use the inline-source-map option, which is good for illustrative purposes (though not for production):

           对于本指南,让我们使用这个inline-source-map选项,这对于说明的目的是很好的(尽管不是用于生产环境):

    webpack.config.js

      const path = require('path');
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      const CleanWebpackPlugin = require('clean-webpack-plugin');
    
      module.exports = {
        entry: {
          app: './src/index.js',
          print: './src/print.js'
        },
    +   devtool: 'inline-source-map',
        plugins: [
          new CleanWebpackPlugin(['dist']),
          new HtmlWebpackPlugin({
            title: 'Development'
          })
        ],
        output: {
          filename: '[name].bundle.js',
          path: path.resolve(__dirname, 'dist')
        }
      };

    Now let's make sure we have something to debug, so let's create an error in our print.js file:

           现在,让我们确定一下我们有一些需要debug的东西,来吧,我们给print.js整一个error(≖ᴗ≖)✧

    src/print.js

      export default function printMe() {
    -   console.log('I get called from print.js!');
    +   cosnole.log('I get called from print.js!');
      }

    Run an npm run build, it should compile to something like this:

           让我们运行npm run build,它应当会编译一些像这样的东西出来:

    Hash: 7bf68ca15f1f2690e2d1
    Version: webpack 3.1.0
    Time: 1224ms
              Asset       Size  Chunks                    Chunk Names
      app.bundle.js    1.44 MB    0, 1  [emitted]  [big]  app
    print.bundle.js    6.43 kB       1  [emitted]         print
         index.html  248 bytes          [emitted]
       [0] ./src/print.js 84 bytes {0} {1} [built]
       [1] ./src/index.js 403 bytes {0} [built]
       [3] (webpack)/buildin/global.js 509 bytes {0} [built]
       [4] (webpack)/buildin/module.js 517 bytes {0} [built]
        + 1 hidden module
    Child html-webpack-plugin for "index.html":
           [2] (webpack)/buildin/global.js 509 bytes {0} [built]
           [3] (webpack)/buildin/module.js 517 bytes {0} [built]
            + 2 hidden modules

    Now open the resulting index.html file in your browser. Click the button and look in your console where the error is displayed. The error should say something like this:

           现在,我们在浏览器中打开最终生成的index.html。单击按钮,然后你就可以看到控制台上报了一个error,这个error应当是说了大概这样的内容:

     Uncaught ReferenceError: cosnole is not defined
        at HTMLButtonElement.printMe (print.js:2)

    We can see that the error also contains a reference to the file (print.js) and line number (2) where the error occurred. This is great, because now we know exactly where to look in order to fix the issue.

           我们能看到这个error也包含了对于print.js的引用,而且明确指出错误是出现在第二行的。这太给力了,这样一来,我们就能确切地知道什么地方出了问题然后修复它。

    Choosing a Development Tool (选一件开发工具)

    Some text editors have a "safe write" function that might interfere with some of the following tools. Read Adjusting Your text Editor for a solution to these issues.

           某些文本编辑器具有“安全写入”功能,可能会干扰以下某些工具。阅读调整您的文本编辑器以解决这些问题。(๑´ㅂ`๑)

    It quickly becomes a hassle to manually run npm run build every time you want to compile your code.

           相信我,很快你就会感觉每次手动运行npm run build非常麻烦。ヽ(`Д´)ノ

    There are a couple of different options available in webpack that help you automatically compile your code whenever it changes:

           webpack中有几个不同的选项可以帮助您在代码更改时自动编译代码:(❁´◡`❁)✲゚

    • webpack's Watch Mode (webpack监视模式)
    • webpack-dev-server (webpack开发服务)
    • webpack-dev-middleware (webpack开发中间件)

    In most cases, you probably would want to use webpack-dev-server, but let's explore all of the above options.

           在大多数情况下,您可能会想要使用webpack-dev-server,但是我们来探索以上所有选项。

    Using Watch Mode (使用webpack监视模式)

    You can instruct webpack to "watch" all files within your dependency graph for changes. If one of these files is updated, the code will be recompiled so you don't have to run the full build manually.

           您可以指示webpack“监视”依赖关系图中的所有文件是否发生了更改。如果其中一个文件更新了,代码会自动重新编译,你不必再手动重新编译了。

    Let's add an npm script that will start webpack's Watch Mode:

           让我们添加一个npm脚本,这样我们就可以开启webpack的监视模式了:

    package.json

      {
        "name": "development",
        "version": "1.0.0",
        "description": "",
        "main": "webpack.config.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
    +     "watch": "webpack --watch",
          "build": "webpack"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "devDependencies": {
          "clean-webpack-plugin": "^0.1.16",
          "css-loader": "^0.28.4",
          "csv-loader": "^2.1.1",
          "file-loader": "^0.11.2",
          "html-webpack-plugin": "^2.29.0",
          "style-loader": "^0.18.2",
          "webpack": "^3.0.0",
          "xml-loader": "^1.2.1"
        }
      }

    You can now run npm run watch from the command line to see that webpack compiles your code, but doesn't exit to the command line. This is because the script is still watching your files.

           现在,你可以在命令提示符下执行npm run watch来观察webpack编译你的代码的过程,但是请不要退出命令行。因为脚本还在运行着,它正在监视着你的文件。

    Now, with webpack watching your files, let's remove the error we introduced earlier:

           现在,就让webpack一直监视着你的文件,让我们移除我们前面介绍到的error

    src/print.js

      export default function printMe() {
    -   cosnole.log('I get called from print.js!');
    +   console.log('I get called from print.js!');
      }

    Now save your file and check the terminal window. You should see that webpack automatically recompiles the changed module!

           现在,保存你的文件,查看你的命令行窗口,你应该会发现webpack正在自动重新编译发生改变的模块!((^∀^*))

    The only downside is that you have to refresh your browser in order to see the changes. It would be much nicer if that would happen automatically as well, so let's try webpack-dev-server which will do exactly that.

           唯一会让你感到消极的是,你需要刷新一下浏览器才能看到改变。要是浏览器也能跟着自动刷新那就棒呆了,所以,让我们尝试一下能实现这个的webpack-dev-server吧!( ̄︶ ̄)↗

    Using webpack-dev-server (使用webpack开发服务)

    The webpack-dev-server provides you with a simple web server and the ability to use live reloading. Let's set it up:

           webpack-dev-server可以为你提供一个简单的web服务器,而且还可以使你的项目自动重加载,让我们来设置一下:

    P.S.:live reloading我这里想翻译成自动重载,虽然在JavaWeb中已经有了热部署和热加载的概念。这里的自动重载有点像热加载,但是两个概念还是有区别的,所以我加以区分,望后端同学谅解。

    npm install --save-dev webpack-dev-server

    Change your config file to tell the dev server where to look for files:

           修改你的配置文件以告诉dev server需要盯紧了哪些文件。 ̄ω ̄=

    webpack.config.js

      const path = require('path');
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      const CleanWebpackPlugin = require('clean-webpack-plugin');
    
      module.exports = {
        entry: {
          app: './src/index.js',
          print: './src/print.js'
        },
        devtool: 'inline-source-map',
    +   devServer: {
    +     contentBase: './dist'
    +   },
        plugins: [
          new CleanWebpackPlugin(['dist']),
          new HtmlWebpackPlugin({
            title: 'Development'
          })
        ],
        output: {
          filename: '[name].bundle.js',
          path: path.resolve(__dirname, 'dist')
        }
      };

    This tells webpack-dev-server to serve the files from the dist directory on localhost:8080.

           这个配置告诉webpack-dev-server部署dist目录下的文件到localhost:8080

    Let's add a script to easily run the dev server as well:

           让我们添加一个脚本,使得dev server也能够轻松运行。 (ノ ̄▽ ̄)

    package.json

      {
        "name": "development",
        "version": "1.0.0",
        "description": "",
        "main": "webpack.config.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "watch": "webpack --progress --watch",
    +     "start": "webpack-dev-server --open",
          "build": "webpack"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "devDependencies": {
          "clean-webpack-plugin": "^0.1.16",
          "css-loader": "^0.28.4",
          "csv-loader": "^2.1.1",
          "file-loader": "^0.11.2",
          "html-webpack-plugin": "^2.29.0",
          "style-loader": "^0.18.2",
          "webpack": "^3.0.0",
          "xml-loader": "^1.2.1"
        }
      }

    Now we can run npm start from the command line and we will see our browser automatically loading up our page. If you now change any of the source files and save them, the web server will automatically reload after the code has been compiled. Give it a try!

           现在,我们可以在命令行中执行npm start,然后我们就可以看到我们的浏览器自动加载我们的页面。如果你修改了任何一个源文件并且保存了,web服务器都将自动地重加载编译后的代码。赶紧试试吧!

    The webpack-dev-server comes with many configurable options. Head over to the documentation to learn more.

           webpack-dev-server带有许多配置选项。转到文档了解更多。

    Now that your server is working, you might want to give Hot Module Replacement a try!

           现在,你的服务器工作起来了,你也许希望尝试一下热模块替换

    Using webpack-dev-middleware (使用webpack开发中间件)

    webpack-dev-middleware is a wrapper that will emit files processed by webpack to a server. This is used in webpack-dev-server internally, however it's available as a separate package to allow more custom setups if desired. We'll take a look at an example that combines webpack-dev-middleware with an express server.

           webpack-dev-middleware是一个将webpack处理后的文件发送到服务器的包装器。它在webpack-dev-server的内部被使用,然而,当你有更多需要自定义的设置的时,它也能被单独使用。让我们来看一个包含webpack-dev-middlewareexpress服务器的例子。

    Let's install express and webpack-dev-middleware so we can get started:

    让我们先安装一下expresswebpack-dev-middleware,让我们能够开始下面的例子。

    npm install --save-dev express webpack-dev-middleware

    Now we need to make some adjustments to our webpack configuration file in order to make sure the middleware will function correctly:

           现在我们需要对我们的webpack配置文件做一些调整,以确认我们的中间件功能正常。

    webpack.config.js

      const path = require('path');
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      const CleanWebpackPlugin = require('clean-webpack-plugin');
    
      module.exports = {
        entry: {
          app: './src/index.js',
          print: './src/print.js'
        },
        devtool: 'inline-source-map',
        plugins: [
          new CleanWebpackPlugin(['dist']),
          new HtmlWebpackPlugin({
            title: 'Output Management'
          })
        ],
        output: {
          filename: '[name].bundle.js',
          path: path.resolve(__dirname, 'dist'),
    +     publicPath: '/'
        }
      };

    The publicPath will be used within our server script as well in order to make sure files are served correctly on http://localhost:3000, the port number we'll specify later. The next step is setting up our custom express server:

           为了确认有哪些文件将被正确部署到http://localhost:3000,publicPath也会被我们的服务脚本所使用。端口号我们会稍后定义。下一步,我们定制化一下我们的express服务器:

    project

      webpack-demo
      |- package.json
      |- webpack.config.js
    + |- server.js
      |- /dist
      |- /src
        |- index.js
        |- print.js
      |- /node_modules

    server.js

    const express = require('express');
    const webpack = require('webpack');
    const webpackDevMiddleware = require('webpack-dev-middleware');
    
    const app = express();
    const config = require('./webpack.config.js');
    const compiler = webpack(config);
    
    // Tell express to use the webpack-dev-middleware and use the webpack.config.js
    // configuration file as a base.
    app.use(webpackDevMiddleware(compiler, {
      publicPath: config.output.publicPath
    }));
    
    // Serve the files on port 3000.
    app.listen(3000, function () {
      console.log('Example app listening on port 3000!\n');
    });

    Now add an npm script to make it a little easier to run the server:

           现在,让我们添加一个npm脚本,让它更便捷地运行在服务器上。

    package.json

      {
        "name": "development",
        "version": "1.0.0",
        "description": "",
        "main": "webpack.config.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "watch": "webpack --progress --watch",
          "start": "webpack-dev-server --open",
    +     "server": "node server.js",
          "build": "webpack"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "devDependencies": {
          "clean-webpack-plugin": "^0.1.16",
          "css-loader": "^0.28.4",
          "csv-loader": "^2.1.1",
          "express": "^4.15.3",
          "file-loader": "^0.11.2",
          "html-webpack-plugin": "^2.29.0",
          "style-loader": "^0.18.2",
          "webpack": "^3.0.0",
          "webpack-dev-middleware": "^1.12.0",
          "xml-loader": "^1.2.1"
        }
      }

    Now in your terminal run npm run server, it should give you an output similar to this:

           现在,在你的终端执行npm run server,它应当给你一个类似于这样的输出:

    Example app listening on port 3000!
    webpack built 27b137af6d9d8668c373 in 1198ms
    Hash: 27b137af6d9d8668c373
    Version: webpack 3.0.0
    Time: 1198ms
              Asset       Size  Chunks                    Chunk Names
      app.bundle.js    1.44 MB    0, 1  [emitted]  [big]  app
    print.bundle.js    6.57 kB       1  [emitted]         print
         index.html  306 bytes          [emitted]
       [0] ./src/print.js 116 bytes {0} {1} [built]
       [1] ./src/index.js 403 bytes {0} [built]
       [2] ./node_modules/lodash/lodash.js 540 kB {0} [built]
       [3] (webpack)/buildin/global.js 509 bytes {0} [built]
       [4] (webpack)/buildin/module.js 517 bytes {0} [built]
    Child html-webpack-plugin for "index.html":
             Asset    Size  Chunks  Chunk Names
        index.html  544 kB       0
           [0] ./node_modules/html-webpack-plugin/lib/loader.js!./node_modules/html-webpack-plugin/default_index.ejs 538 bytes {0} [built]
           [1] ./node_modules/lodash/lodash.js 540 kB {0} [built]
           [2] (webpack)/buildin/global.js 509 bytes {0} [built]
           [3] (webpack)/buildin/module.js 517 bytes {0} [built]
    webpack: Compiled successfully.

    Now fire up your browser and go to http://localhost:3000, you should see your webpack app running and functioning!

           现在,打开浏览器,访问http://localhost:3000,你应当看到你的wepback应用正在运行和发挥作用!ヽ(゚∀゚)メ(゚∀゚)ノ

    If you would like to know more about how Hot Module Replacement works, we recommend you read the Hot Module Replacement guide.

           如果你对于模块热重置如何工作很感兴趣,我们推荐你阅读Hot Module Replacement guide

    Adjusting Your Text Editor

    When using automatic compilation of your code, you could run into issues when saving your files. Some editors have a "safe write" feature that can potentially interfere with recompilation.

           当你正在使用代码自动补全的时候,你可能会在保存文件时陷入麻烦。有一些编辑器有“安全写入”的特性,啥意思呢?它会潜在地扰乱重新编译。

    To disable this feature in some common editors, see the list below:

           如果想在一些普通的编辑器中关闭这个特性,看看下面这个列表吧:

    • Sublime Text 3 - 添加 atomic_save: "false" 到你的用户偏好.
    • IntelliJ - 在偏好中找到 safe write ,然后使之失效.
    • Vim - 在设置中添加backupcopy=yes.
    • WebStorm - 在 Preferences > Appearance & Behavior > System Settings取消选中safe write.

    Conclusion (总结)

    Now that you've learned how to automatically compile your code and run a simple development server, you can check out the next guide, which will cover Hot Module Replacement.

    现在,你已经学到了如歌自动编译你的代码,运行在一个简单的开发服务器上了。捏可以看看下个指南,它会讲一下模块热重置

    P.S.:到了这里,我们使用webpack进行开发已经不存在很大的问题了。只是我们应该一鼓作气|●´∀`|σ,继续探索一下更加深入的内容。

    展开全文
  • firebase教程In one of my previous articles, I discussed how you can create a REST API using Firebase Functions. Since then, I have been asked how to authenticate requests and validate data on my ...
  • Android Studio常见问题以及解决方式

    万次阅读 多人点赞 2016-09-14 10:53:33
    Error:Failed to create directory 'C:\Users\Zhang\.gradle\caches\2.4\scripts\ijinit2_ejtybjqv7774bve37wtrho5a5\InitScript\initscript\classes'   同步一下工程就好了 32.签名步骤:    ...
  • Sentry搭建和使用

    千次阅读 2020-11-29 13:35:52
    It is not intended to work in # all use-cases and must be adapted to fit your needs. This is merely # a guideline. # See docs.getsentry....
  • sublim插件(待续)

    2018-03-21 22:53:00
    Just add a "Minify" object to the "settings" object in the project's .sublime-project file containing your project specific settings. 你也可以为独立项目覆盖默认和用户设置,在setting心中添加minify对象...
  • PROJECT_NUMBER = "1.0.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea ...
  • 另外还有一种方法是在project选中jar包点击右键”Add as library”,这种方法需要定位到项目的根目录,即 build.gradle所在的目录。 dependencies { compile files('libs/android-support-v4.jar') ...
  • Hbase官方文档中文版

    千次阅读 2015-03-09 14:44:49
    starting Master, logging to logs/hbase-user-master-example.org.out 现在你运行的是单机模式的Hbaes。所有的服务都运行在一个JVM上,包括HBase和Zookeeper。HBase的日志放在 logs 目录,当你启动出问题的时候...
  • HBase 官方文档中文版

    千次阅读 2016-10-24 15:39:58
    starting Master, logging to logs/hbase-user-master-example.org.out 现在你运行的是单机模式的Hbaes。所有的服务都运行在一个JVM上,包括HBase和Zookeeper。HBase的日志放在 logs 目录,当你启动出问题的时候,...
  • hbase官方文档中文版0.97

    千次阅读 2017-07-29 09:38:27
    starting Master, logging to logs/hbase-user-master-example.org.out 现在你运行的是单机模式的Hbaes。所有的服务都运行在一个JVM上,包括HBase和Zookeeper。HBase的日志放在 logs 目录,当你启动出问题的时候,...
  • Android进阶之开发问题笔记

    千次阅读 2017-06-26 22:37:40
    搜索页面Full Search中输入“getDlWarning”,右边的In Projects选择“select all”,接着点击“search”,从搜索结果中找到后缀名带有c的文件(.cpp,.cc等等)即可 (4)学习链接 IT工具 查看native层源码 1.3 ...
  • hbase0.95 官方文档

    千次阅读 2014-07-31 10:17:54
    starting Master, logging to logs/hbase-user-master-example.org.out 现在你运行的是单机模式的Hbaes。所有的服务都运行在一个JVM上,包括HBase和Zookeeper。HBase的日志放在 logs 目录,当你启动出问题的时候,...
  • HBase 官方文档0.97.0

    万次阅读 2013-05-29 17:57:55
    starting Master, logging to logs/hbase-user-master-example.org.out 现在你运行的是单机模式的Hbaes。所有的服务都运行在一个JVM上,包括HBase和Zookeeper。HBase的日志放在 logs 目录,当你启动出问题的时候,...
  • Just add a "Minify" object to the "settings" object in the project's .sublime-project file containing your project specific settings. 你也可以为独立项目覆盖默认和用户设置,在setting心中添加minify对象...
  • GRPC go与C++通信

    2021-09-25 22:06:59
    # This approach is very simple to use, but the are some potential # disadvantages: # * it includes gRPC's CMakeLists.txt directly into your build script # without and that can make gRPC's internal ...
  • 工作中的一些错误解决记录

    万次阅读 2020-01-21 13:54:03
    Please ensure that your table name is correct.  2)数据库表没有主键时,需要指定--split-by参数或者使用--autoreset-to-one-mapper参数;  Error during import: No primary key could be found for table ...
  • Java后台日常学习环境搭建

    千次阅读 2020-09-23 14:20:51
    $ git config --global user.name "onlyou2030" $ git config --global user.email 529647632@qq.com cd到要上传的仓库路径,添加远程地址 git remote add origin git@github.com:yourName/yourRepo.git 添加,提交...
  • 用web技术写APP

    2018-08-22 17:48:00
    Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager. Alternatively, to learn how to ...
  • Set 'spring.devtools.add-properties' to 'false' to disable 2020-03-11 19:59:05.039 INFO [ms-user,,,] 10120 --- [ restartedMain] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework....
  • Use your judgment, and if you are unsure, please don’t hesitate to ask your project leads to get additional input. 1.头文件 通常每一个 .cc 文件都有一个对应的 .h 文件. 也有一些常见例外, ...
  • "you must provide a page info to display"); } SimpleBackPage page = SimpleBackPage.getPageByValue(pageValue); if (page == null) { throw new IllegalArgumentException("can not find page by value:" ...

空空如也

空空如也

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

pleaseaddoneofittoyourproject线上报错